FerrumPixel
Rust-accelerated image preprocessing for Python.
Install · Quickstart · API reference · Semantics · Concurrency · Performance
FerrumPixel gives you the operations an ML pipeline actually needs — resize, crop, rotate, brightness, contrast, equalize_histogram, sharpen, normalize — behind a small chainable API that hands you a NumPy array at the end. The pixel work happens in Rust, with the GIL released for the whole of every call. It is built for the layer between your web framework and your model, where preprocessing runs on every request and the GIL decides how much of your machine you actually get to use.
import ferrumpixel as fp
arr = (fp.load("image.jpg")
.resize(512, 512)
.sharpen()
.normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
.numpy()) # (512, 512, 3) float32, RGB
Table of contents
- What it replaces, and why
- Install
- Quickstart
- Core concepts
- API reference
- Pixel semantics
- Concurrency and threading
- Error handling
- Type checking
- Performance
- Known deviations and limitations
- Versioning and stability
- Project documentation
What it replaces, and why
The pipeline FerrumPixel targets is the familiar PIL → OpenCV → NumPy chain: open with PIL, convert to an array, run a few OpenCV ops, normalize with NumPy. That works, but it carries three costs.
The GIL. Those libraries release the GIL unevenly, and a mixed chain reacquires it repeatedly, so preprocessing serialises against the rest of your process. FerrumPixel releases the GIL for the entire duration of every call, single-image and batch alike. In a threaded server that is the difference between preprocessing blocking your request handlers and running alongside them.
Copies. Each hand-off between PIL, OpenCV and NumPy is a conversion and usually a copy. FerrumPixel keeps one buffer in Rust for the whole chain and copies out exactly once, when you call .numpy().
Undefined semantics. In the usual chain, rotation direction, contrast pivot and rounding behaviour depend on which library you happened to reach for. FerrumPixel fixes them once and versions them — see Pixel semantics.
Batches are the case FerrumPixel is strongest at: they dispatch across Rayon's thread pool with the GIL released for the whole call, so throughput scales with cores rather than with the interpreter.
Install
pip install ferrumpixel
Wheels are abi3, so a single wheel per platform covers CPython 3.9 through 3.13. No Rust toolchain is needed to install.
| Platform | Architectures |
|---|---|
| Linux (manylinux 2.17+) | x86_64, aarch64 |
| macOS | x86_64, arm64 |
| Windows | x64 |
Only dependency is numpy. On a platform without a prebuilt wheel, pip falls back to the sdist, which requires a Rust toolchain (1.70+).
CPython only.
abi3is a CPython ABI, so PyPy is not supported.
Quickstart
import ferrumpixel as fp
# Load from a path, or straight from bytes — an upload body, an S3 object, ...
image = fp.load("image.jpg")
image = fp.load(request_body_bytes)
# Chain operations; nothing is computed lazily, each call does its work now
arr = (fp.load("image.jpg")
.resize(512, 512, fp.Interpolation.BILINEAR)
.crop(0, 0, 480, 480)
.rotate(90)
.brightness(0.1)
.contrast(1.2)
.equalize_histogram()
.sharpen(strength=0.5)
.normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
.numpy())
arr.shape, arr.dtype # ((480, 480, 3), dtype('float32'))
To write an image back out, skip normalize — it converts to float32, and save needs the uint8 path:
fp.load("image.jpg").resize(512, 512).sharpen().save("optimized.jpg")
Batches
load_batch decodes in parallel, every operation runs in parallel, and .numpy() stacks the result into one tensor:
arr = (fp.load_batch(["a.jpg", "b.jpg", "c.jpg"])
.resize(224, 224)
.normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
.numpy())
arr.shape # (3, 224, 224, 3) -> (N, H, W, C)
Stacking requires uniform dimensions, so resize before .numpy(). A ragged batch raises ValueError rather than padding or guessing.
Core concepts
The chain
Every operation returns something chainable, and every call executes immediately — there is no lazy graph and no .compute(). .numpy() and .save() are the terminal operations that get data back out of Rust.
Input is always converted to 8-bit RGB on load:
| Input | Result |
|---|---|
| RGB | unchanged |
| Grayscale | expanded to 3 identical channels |
| RGBA / palette with alpha | alpha is dropped, not composited |
If you need alpha-aware compositing, do it before handing the image to FerrumPixel.
The aliasing contract
This is the one part of the API that can surprise you, so it is worth reading once.
Operations fall into two groups:
- Shape- or dtype-changing —
resize,crop,rotate,sharpen,normalize. These allocate and return a newImage. The receiver is untouched. - Neither —
brightness,contrast,equalize_histogram. These mutate the buffer in place and return the same object.
a = fp.load("x.jpg")
b = a.resize(256, 256) # b is independent; a is unchanged
c = a.brightness(0.1) # c IS a -> True; a was modified too
Chained left to right this never bites you, because you never hold an intermediate:
result = fp.load("x.jpg").resize(256, 256).brightness(0.1).numpy() # fine
It only matters if you bind an intermediate and expect it to stay put:
base = fp.load("x.jpg")
thumb = base.resize(64, 64) # safe: new object
bright = base.brightness(0.2) # base is now brighter too
dark = base.brightness(-0.2) # applied on top of the previous change
The reason for the split is allocation: forcing brightness to allocate a full copy would measurably hurt long chains for no benefit in the common case. .clone() is specified in design.md §1.3 as the escape hatch but is not yet implemented — until it lands, call fp.load(...) twice if you need two independent handles.
API reference
fp.load(path_or_bytes)
Decode a single image.
| Parameter | Type | Description |
|---|---|---|
path_or_bytes |
str | bytes |
A filesystem path, or the encoded bytes of an image |
Returns Image, always 8-bit RGB.
Formats: PNG, JPEG, BMP, WebP, TIFF and GIF all decode. PNG and JPEG are the formats under test in CI.
Raises OSError if the path is missing or unreadable, ValueError if the data is not a decodable image, TypeError if the argument is neither str nor bytes.
The GIL is released for the decode.
fp.load_batch(paths_or_bytes)
Decode many images in parallel.
| Parameter | Type | Description |
|---|---|---|
paths_or_bytes |
Sequence[str | bytes] |
Paths, encoded bytes, or a mix |
Returns BatchImage, in the order given.
Raises the same exceptions as load. A single bad entry fails the whole call rather than yielding a partial batch, so the returned batch always has exactly one image per input.
fp.Interpolation
Resampling filter for resize. Members are upper-case per PEP 8.
| Member | Notes |
|---|---|
NEAREST |
Fastest, blocky. Use for masks and label maps, where interpolating between class indices would invent values that mean nothing. |
BILINEAR |
Default. Good quality for preprocessing, cheap. |
BICUBIC |
Catmull-Rom. Sharper than bilinear on upscale. |
LANCZOS3 |
Highest quality, most expensive. Best for large downscales. |
fp.Image
A single decoded image. Construct with load, never directly.
Columns: New? indicates whether the method returns a new object (per the aliasing contract).
| Method | Signature | New? |
|---|---|---|
resize |
(w, h, interpolation=BILINEAR) |
new |
crop |
(x, y, w, h) |
new |
rotate |
(degrees, expand=False) |
new |
sharpen |
(strength=1.0) |
new |
normalize |
(mean, std) |
new |
brightness |
(delta) |
in place |
contrast |
(factor) |
in place |
equalize_histogram |
() |
in place |
numpy |
() |
— |
save |
(path) |
— |
resize(w, h, interpolation=BILINEAR)
Resize to exactly w × h pixels. Aspect ratio is not preserved — the target size is used as given. Compute the aspect-correct size yourself if you need it.
Raises ValueError if either dimension is 0.
img.resize(224, 224)
img.resize(1024, 768, fp.Interpolation.LANCZOS3)
crop(x, y, w, h)
Crop a w × h region whose top-left corner is at (x, y), measured from the top-left of the image.
Raises ValueError if the region extends past the bounds. It is never silently clamped — a crop that does not fit is a bug in the caller, not something to paper over.
rotate(degrees, expand=False)
Rotate counter-clockwise for positive angles, matching PIL. (OpenCV's rotate constants turn the other way — see Pixel semantics.)
expand |
Behaviour |
|---|---|
False (default) |
Keep the original dimensions; corners rotate out of frame and are lost |
True |
Grow the canvas so the whole rotated image fits |
Newly exposed area is filled with black. Rotation always resamples into a fresh buffer, so it returns a new object regardless of expand.
img.rotate(90) # 640x480 -> 640x480, corners clipped
img.rotate(30, expand=True) # 640x480 -> 795x736
sharpen(strength=1.0)
Sharpen with a 3×3 convolution, blending between the identity kernel and PIL's ImageFilter.SHARPEN.
strength |
Effect |
|---|---|
0.0 |
No-op |
1.0 |
Matches PIL's SHARPEN (default) |
> 1.0 |
Over-sharpens |
Known deviation: the outermost 1-pixel ring is zero-filled (black), because out-of-bounds taps are treated as zero rather than replicated. Crop 1 pixel afterwards if that border matters. See Known deviations.
normalize(mean, std)
Convert to float32 as (pixel / 255 - mean) / std, applied per channel.
| Parameter | Type | Description |
|---|---|---|
mean |
Sequence[float] |
Exactly 3 per-channel means, RGB order |
std |
Sequence[float] |
Exactly 3 per-channel standard deviations |
This changes dtype from uint8 to float32, so it is normally the last step before .numpy(). Afterwards save and all the uint8-only operations are unavailable. Raises ValueError unless both sequences have exactly 3 elements.
# ImageNet statistics
img.normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
brightness(delta)
Add delta to every channel, where delta is a fraction of full scale — 0.1 shifts by 25.5 of 255 levels. Negative darkens. Results are clamped to the valid range, not wrapped.
Mutates in place and returns self.
contrast(factor)
Scale contrast about mid-grey: each channel becomes (v - 128) * factor + 128, clamped. 1.0 is a no-op, above 1 increases contrast, below 1 flattens toward grey.
Mutates in place and returns self.
The pivot is 128, not 0. OpenCV's
convertScaleAbsscales about 0, so the samefactorgives different results in the two libraries.
equalize_histogram()
Equalize the histogram of each channel independently.
Mutates in place and returns self.
Operating per-channel on RGB rather than on a luminance channel can shift colour balance on strongly tinted images. That is intentional and fixed as of 1.0.0.
numpy()
Copy the buffer out as a NumPy array of shape (H, W, C) in RGB order. dtype is uint8, or float32 once normalize has been applied.
This copies rather than sharing memory, so the array stays valid and independent no matter what happens to the Image afterwards.
save(path)
Encode and write to path. Format is chosen from the extension; PNG, JPEG, BMP, WebP, TIFF and GIF all work.
Only valid on the uint8 path. Calling it after normalize raises ValueError, because a normalized float buffer has no meaningful encoding as an 8-bit image. Raises OSError if the path is unwritable.
fp.BatchImage
Images transformed together in parallel. Construct with load_batch, never directly.
Every method mirrors Image with identical parameters and the same aliasing behaviour, applied across the whole batch. The only differences:
- No
save. Write images individually if you need files on disk. numpy()returns(N, H, W, C)and requires uniform shape and dtype across the batch.
batch = fp.load_batch(paths)
arr = (batch
.resize(224, 224) # new batch
.brightness(0.1) # in place, returns the same batch
.normalize(mean=..., std=...)
.numpy()) # (N, 224, 224, 3) float32
numpy() raises ValueError if the batch is empty, or if its images disagree on shape or dtype. Ragged batches are out of scope; call resize first to make them uniform.
Errors short-circuit: if crop falls outside any one image, the whole call fails rather than returning partial results.
Pixel semantics
These are fixed as of 1.0.0. Changing any of them is a major version bump, however small the diff — see techstack.md §6.
| Behaviour | FerrumPixel | Note |
|---|---|---|
| Rotation direction | Counter-clockwise for positive angles | PIL convention. OpenCV's ROTATE_90_CLOCKWISE turns the other way |
| Rotation centre | Pixel-grid centre, ((w-1)/2, (h-1)/2) |
Not w/2, which is off by half a pixel |
| Contrast pivot | Mid-grey: (v - 128) * factor + 128 |
OpenCV's convertScaleAbs scales about 0 |
| Brightness | v + delta * 255, clamped |
delta is a 0–1 fraction, not a raw level |
| Normalize | (pixel / 255 - mean) / std, float32 |
Per channel, RGB order |
| Rounding | f32 → u8 rounds |
Does not truncate |
| Channel order | RGB throughout | Not BGR |
| Histogram equalization | Per channel on RGB | Not on a luminance channel |
Every operation is parity-tested against a PIL → OpenCV → NumPy reference within documented per-op tolerances, and the benchmark suite refuses to report timings if that gate fails. Methodology in benchmark.md §7.
Concurrency and threading
GIL release
Every operation wraps its compute in py.allow_threads, so the GIL is free for the whole call. Other Python threads make progress while FerrumPixel works. This does not parallelise a single image internally — it prevents one slow transform from stalling the interpreter.
Batch parallelism
BatchImage dispatches across Rayon's global thread pool. The pool defaults to one worker per logical CPU.
export RAYON_NUM_THREADS=4
Containers: set this explicitly. Rayon sizes its pool from the logical CPU count of the host and does not read cgroup CPU limits. In a container with a 2-CPU quota on a 64-core host, it will spawn 64 workers and thrash. Pin
RAYON_NUM_THREADSto your quota.
Thread count affects scheduling only, never output — this is covered by a test.
Web frameworks
In FastAPI or any ASGI framework, declare preprocessing handlers as plain def, not async def:
@app.post("/preprocess")
def preprocess(file: UploadFile = File(...)): # def, not async def
arr = fp.load(file.file.read()).resize(512, 512).numpy()
return {"shape": arr.shape}
An async def handler runs directly on the event loop, so synchronous CPU-bound work inside it blocks every other in-flight request — the service then handles exactly one request at a time no matter how many clients connect. A plain def handler is dispatched to the threadpool, which is what lets the GIL release turn into actual parallelism. In our own load test this was the difference between throughput flat at ~34 req/s and throughput scaling from 23 to 140 req/s across a 1→100 concurrency ramp.
A complete worked example is in examples/fastapi_service/.
Error handling
Everything maps onto Python builtins — there is no custom exception hierarchy to learn or catch.
| Condition | Raises |
|---|---|
| Missing or unreadable path | OSError |
Unwritable destination in save |
OSError |
| Undecodable image data | ValueError |
| Crop outside image bounds | ValueError |
mean/std not exactly 3 elements |
ValueError |
save after normalize |
ValueError |
Empty or ragged batch in numpy() |
ValueError |
Argument neither str nor bytes |
TypeError |
Type checking
The package ships inline type stubs and a PEP 561 py.typed marker, so mypy and pyright pick up the API with no extra stub package:
img: fp.Image = fp.load("a.jpg")
arr = img.resize(512, 512).numpy()
fp.load(123) # error: no overload matches argument type "int"
img.resize("512", 512) # error: incompatible type "str"; expected "int"
Performance
Measured on an 8-core Zen 3 machine. Full methodology and numbers in benchmark.md and the Phase 4 report.
| Workload | vs. PIL/OpenCV/NumPy baseline |
|---|---|
| Batch full chain, N=128 | 1.88x faster |
| Batch full chain, N=8 | 1.42x faster |
resize alone |
1.90x faster |
| Peak RSS during batch | 0.88x (uses less memory) |
| Single-image full chain | 0.54x — slower |
Two honest observations.
Batch is where FerrumPixel wins, and the advantage grows with batch size — 1.42x at N=8 to 1.88x at N=128 — because Rayon's dispatch overhead amortises over more work while the baseline's per-image throughput stays flat.
Single-image full chains are currently slower than OpenCV. The cause is sharpen: it runs a scalar 3×3 convolution against OpenCV's hand-tuned SIMD filter2D, and at ~51 ms on a 1024² image it dominates the chain. If your workload is single-image and sharpen-heavy, OpenCV is still faster today. Vectorising it is the top item on the post-1.0 list. Chains without sharpen fare considerably better.
Under concurrent load the picture is different again: FerrumPixel holds a lower p99 than the baseline at high concurrency (1200 ms vs 1900 ms at 100 concurrent clients) because the GIL release keeps the tail from blowing out, even though median throughput still favours the baseline.
Known deviations and limitations
These are documented behaviour as of 1.0.0, not pending fixes. Correcting the first two would change output pixels and therefore requires a major version bump.
sharpenleaves a 1-pixel zero-filled border. OpenCV reflects and PIL replicates at the edge; FerrumPixel treats out-of-bounds taps as zero. Interior pixels match OpenCV to within 1 level.equalize_histogramnormalises its CDF bytotal, where OpenCV usestotal - cdf_min. Outputs differ by up to 10 levels on the reference image. This is an accepted algorithmic divergence, not a bug..clone()is not implemented. It is specified in design.md §1.3 as the escape hatch from in-place aliasing. Additive, so it can land in a 1.x release.- Ragged batches are unsupported.
BatchImage.numpy()raises rather than padding or returning a ragged structure. - Alpha is dropped on load, not composited.
- No GPU support, and no zero-copy NumPy interop —
.numpy()always copies. - PyPy is unsupported, since
abi3is a CPython ABI.
Versioning and stability
Semantic versioning, with one project-specific rule: any change to pixel semantics is a major bump, regardless of how small the code change is. Interpolation defaults, rounding, channel order, rotation direction and contrast pivot are all covered.
- Major — pixel semantics change, or an API removal
- Minor — new operations, new optional keyword arguments
- Patch — bug fixes that do not change documented behaviour
Full policy in techstack.md §6. Release history in CHANGELOG.md.
Project documentation
| Doc | Purpose |
|---|---|
| design.md | API design — signatures, aliasing semantics, pixel-semantics contract |
| benchmark.md | Benchmark methodology, target numbers, correctness gate |
| architecture.md | Component boundaries, data flow, concurrency model |
| prd.md | Goals, non-goals, user stories, success metrics |
| techstack.md | Every dependency, why it was chosen, versioning policy |
| libraryScope.md | In-scope/out-of-scope boundaries per dependency, rejected alternatives |
| phases.md | Implementation roadmap and per-phase exit criteria |
| agents.md | Conventions and guardrails for AI coding agents working in this repo |
| CHANGELOG.md | Version history |
Contributors: start with prd.md → architecture.md → design.md → techstack.md. AI agents: read agents.md first, always.
Building from source
git clone https://github.com/ShivamMalge/FerrumPixel.git
cd FerrumPixel
python -m venv .venv && source .venv/bin/activate
pip install maturin pytest numpy pillow opencv-python-headless pytest-benchmark
maturin develop --release -m crates/ferrumpixel-py/Cargo.toml
pytest tests/python -v # test suite
pytest benches/test_bench.py --benchmark-disable # correctness gate
cargo test --workspace # Rust tests
cargo clippy --workspace -- -D warnings
The correctness gate must pass before any benchmark will report a number — a fast wrong answer is not a result.
License
Dual licensed under MIT or Apache-2.0, at your option.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distributions
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file ferrumpixel-1.0.0.tar.gz.
File metadata
- Download URL: ferrumpixel-1.0.0.tar.gz
- Upload date:
- Size: 51.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
870ba0f241f2a57e475092c917f97297a560e638706ddb675c55d857a8de7f8a
|
|
| MD5 |
fb7c43d670fcb57ccfe9ccea396b0cdd
|
|
| BLAKE2b-256 |
ca6e9672bc5c1d7f153bb065ffd6e8838de32ca9a211e9774493bb5e017b1d22
|
Provenance
The following attestation bundles were made for ferrumpixel-1.0.0.tar.gz:
Publisher:
release.yml on ShivamMalge/FerrumPixel
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ferrumpixel-1.0.0.tar.gz -
Subject digest:
870ba0f241f2a57e475092c917f97297a560e638706ddb675c55d857a8de7f8a - Sigstore transparency entry: 2550368593
- Sigstore integration time:
-
Permalink:
ShivamMalge/FerrumPixel@85d551e66c4239382fd022fb6a3d7082cfca886d -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/ShivamMalge
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@85d551e66c4239382fd022fb6a3d7082cfca886d -
Trigger Event:
push
-
Statement type:
File details
Details for the file ferrumpixel-1.0.0-cp39-abi3-win_amd64.whl.
File metadata
- Download URL: ferrumpixel-1.0.0-cp39-abi3-win_amd64.whl
- Upload date:
- Size: 2.9 MB
- Tags: CPython 3.9+, Windows x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
35b944a832d606ddd9229a94c7a17176ffaf788a8acf6abc324d88723494c5f1
|
|
| MD5 |
1531915c9ea4085bc2885fec0cfc6b20
|
|
| BLAKE2b-256 |
02ab2e61311717fbc02ddab2ce18a1b009d38551df320d72f1653219e05ef3de
|
Provenance
The following attestation bundles were made for ferrumpixel-1.0.0-cp39-abi3-win_amd64.whl:
Publisher:
release.yml on ShivamMalge/FerrumPixel
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ferrumpixel-1.0.0-cp39-abi3-win_amd64.whl -
Subject digest:
35b944a832d606ddd9229a94c7a17176ffaf788a8acf6abc324d88723494c5f1 - Sigstore transparency entry: 2550368666
- Sigstore integration time:
-
Permalink:
ShivamMalge/FerrumPixel@85d551e66c4239382fd022fb6a3d7082cfca886d -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/ShivamMalge
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@85d551e66c4239382fd022fb6a3d7082cfca886d -
Trigger Event:
push
-
Statement type:
File details
Details for the file ferrumpixel-1.0.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.
File metadata
- Download URL: ferrumpixel-1.0.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- Upload date:
- Size: 3.3 MB
- Tags: CPython 3.9+, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a2093f9a4fa253b1b95bea3629d106db13ae492093423a544b8bf76b3321c8cd
|
|
| MD5 |
73bf8f86bcd517168fa8a73f17b0b7dd
|
|
| BLAKE2b-256 |
05e0e383f16f36cdd4ecd4a3733b7ebe1e03938e6227d76a91ae5b01cdf6db1d
|
Provenance
The following attestation bundles were made for ferrumpixel-1.0.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:
Publisher:
release.yml on ShivamMalge/FerrumPixel
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ferrumpixel-1.0.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl -
Subject digest:
a2093f9a4fa253b1b95bea3629d106db13ae492093423a544b8bf76b3321c8cd - Sigstore transparency entry: 2550368726
- Sigstore integration time:
-
Permalink:
ShivamMalge/FerrumPixel@85d551e66c4239382fd022fb6a3d7082cfca886d -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/ShivamMalge
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@85d551e66c4239382fd022fb6a3d7082cfca886d -
Trigger Event:
push
-
Statement type:
File details
Details for the file ferrumpixel-1.0.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.
File metadata
- Download URL: ferrumpixel-1.0.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
- Upload date:
- Size: 3.1 MB
- Tags: CPython 3.9+, manylinux: glibc 2.17+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
56c1d604194620d950bb396ee46af22a6d6ba31ef85e6a3f62fd0f9447c9267b
|
|
| MD5 |
4600f1d5796103fb06ece6764f4ee1cc
|
|
| BLAKE2b-256 |
1f752d0351e8a3b0bbb23231ef98db3bf2cc97a459d2089dfe8890bd1346cd4b
|
Provenance
The following attestation bundles were made for ferrumpixel-1.0.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl:
Publisher:
release.yml on ShivamMalge/FerrumPixel
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ferrumpixel-1.0.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl -
Subject digest:
56c1d604194620d950bb396ee46af22a6d6ba31ef85e6a3f62fd0f9447c9267b - Sigstore transparency entry: 2550368626
- Sigstore integration time:
-
Permalink:
ShivamMalge/FerrumPixel@85d551e66c4239382fd022fb6a3d7082cfca886d -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/ShivamMalge
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@85d551e66c4239382fd022fb6a3d7082cfca886d -
Trigger Event:
push
-
Statement type:
File details
Details for the file ferrumpixel-1.0.0-cp39-abi3-macosx_11_0_arm64.whl.
File metadata
- Download URL: ferrumpixel-1.0.0-cp39-abi3-macosx_11_0_arm64.whl
- Upload date:
- Size: 2.9 MB
- Tags: CPython 3.9+, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0dba376a8dc868d44b4320cd53fa23152bfe60086d2b9607bf60f1e46c54bc8b
|
|
| MD5 |
2e6550c1242273030ee9805f1cfd1822
|
|
| BLAKE2b-256 |
c2039ab335fe2b72c6a92ec9b778f822ae906bb6a8b62d573c5546c936838c91
|
Provenance
The following attestation bundles were made for ferrumpixel-1.0.0-cp39-abi3-macosx_11_0_arm64.whl:
Publisher:
release.yml on ShivamMalge/FerrumPixel
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ferrumpixel-1.0.0-cp39-abi3-macosx_11_0_arm64.whl -
Subject digest:
0dba376a8dc868d44b4320cd53fa23152bfe60086d2b9607bf60f1e46c54bc8b - Sigstore transparency entry: 2550368784
- Sigstore integration time:
-
Permalink:
ShivamMalge/FerrumPixel@85d551e66c4239382fd022fb6a3d7082cfca886d -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/ShivamMalge
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@85d551e66c4239382fd022fb6a3d7082cfca886d -
Trigger Event:
push
-
Statement type:
File details
Details for the file ferrumpixel-1.0.0-cp39-abi3-macosx_10_12_x86_64.whl.
File metadata
- Download URL: ferrumpixel-1.0.0-cp39-abi3-macosx_10_12_x86_64.whl
- Upload date:
- Size: 3.1 MB
- Tags: CPython 3.9+, macOS 10.12+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8b88cc6fa6a3694b5373253327df0efcaedf2f1dd2cff8b72e0e702bb44f1727
|
|
| MD5 |
3a401654ab7179bb87087ad9c84095da
|
|
| BLAKE2b-256 |
e1a7c1faea66eb30676b3f70dc47f216df0aa0ce4aef0e18b55ab2ba9d1c7b32
|
Provenance
The following attestation bundles were made for ferrumpixel-1.0.0-cp39-abi3-macosx_10_12_x86_64.whl:
Publisher:
release.yml on ShivamMalge/FerrumPixel
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ferrumpixel-1.0.0-cp39-abi3-macosx_10_12_x86_64.whl -
Subject digest:
8b88cc6fa6a3694b5373253327df0efcaedf2f1dd2cff8b72e0e702bb44f1727 - Sigstore transparency entry: 2550368819
- Sigstore integration time:
-
Permalink:
ShivamMalge/FerrumPixel@85d551e66c4239382fd022fb6a3d7082cfca886d -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/ShivamMalge
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@85d551e66c4239382fd022fb6a3d7082cfca886d -
Trigger Event:
push
-
Statement type: