Skip to main content

focal

Image processing in C++20 with a zero-copy NumPy interface.

pip install focalpy
import focal

img = focal.imread("photo.jpg")            # linear float32, (h, w, c)
img = focal.exposure(img, 0.5)             # half a stop brighter
img = focal.resize(img, 1080, 1920)        # prefiltered, in linear light
img = focal.gaussian_blur(img, 2.0)
focal.imwrite("out.png", img)              # encodes back to sRGB

Why

Downscaling prefilters, so fine detail averages instead of aliasing. Shrinking by 4x means each output pixel must draw on about 4 input pixels. A resize that skips this doesn't lose fine detail gracefully — it invents patterns that were never in the image.

aliasing comparison

Filtering happens in linear light. Blurring and resizing mix pixels together, and mixing light is physics — it has to happen on linear values, not gamma-encoded ones. imread decodes sRGB on the way in and imwrite encodes on the way out, so every op in between is correct by construction.

linear light comparison

Averaging equal black and white gives half the light, which displays as light grey (182/255). Doing it on encoded values gives 128/255 — visibly too dark, and it compounds every time an image is resized.

Nothing converts silently. Array arguments are declared .noconvert(), so a float64 or non-contiguous array raises TypeError rather than quietly costing a full-size copy. Converting is the caller's decision, not a hidden one.

What's here

I/O imread, imwrite — PNG, JPEG, BMP, TGA
Resize resize — nearest, bilinear, Catmull-Rom, Mitchell, Lanczos3
Filtering gaussian_blur, correlate1d, separable_filter
Point ops gamma, exposure, brightness_contrast, invert, clip, blend, apply_lut
Sampling sample — nearest and bilinear at fractional coordinates
Color rgb_to_gray, gray_to_rgb, rgb_to_hsv, rgb_to_xyz, rgb_to_lab and inverses
Transfer srgb_to_linear, linear_to_srgb

Box blur, Sobel, and unsharp mask fall out of the primitives above:

box    = focal.separable_filter(img, k, k)                    # k = ones(n)/n
sobel  = focal.separable_filter(img, [-1,0,1], [1,2,1])
sharp  = focal.blend(img, focal.gaussian_blur(img, 2.0), -0.5)

Border modes (clamp, reflect, reflect101, wrap, constant) are shared by every spatial op and match scipy.ndimage's semantics.

Each color conversion states which light it expects, because the answer is different per space. rgb_to_gray, rgb_to_xyz and rgb_to_lab take linear light — luminance weights (0.2126, 0.7152, 0.0722) are weights on light, and the XYZ matrix is defined on linear sRGB. rgb_to_hsv takes display-referred values, because that is the convention the space was defined under. Nothing is clamped: an XYZ or Lab color outside the sRGB gamut comes back with negative channels rather than silently snapping into range.

Design

Everything is a view. The core type is a non-owning {pointer, dtype, shape, strides} struct, so a crop, a single channel, and a NumPy array handed over from Python are all the same type, and none of them copy. Strides are in bytes, matching NumPy exactly, so wrapping an incoming array is copying six integers.

Ops run on float32 linear. dtype conversion happens at the edges of a pipeline, never per pixel inside an op.

Correctness is checked against independent oracles. The test suite compares against scipy.ndimage and PIL wherever one exists — border handling is verified for every index from -2n to 3n across four modes and four image sizes, and resize matches PIL's interior to 1e-7.

Building from source

Needs a C++20 compiler and CMake:

git clone https://github.com/edisons3608/focal
cd focal
pip install -e . --no-build-isolation
pytest

OpenMP is used when available (-DFOCAL_USE_OPENMP=OFF to disable). On macOS that means brew install libomp.

Status

Early, and the API will change. Missing so far: geometric warps, histogram operations, 16-bit and HDR file formats.

License

MIT. Bundles stb (public domain).

Metadata

Release files for focalpy 0.0.3

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for focalpy 0.0.3
File Size Uploaded Provenance Actions
focalpy-0.0.3.tar.gz 261.3 kB With provenance Details for focalpy-0.0.3.tar.gz

Built distributions (wheels)

Table of built distributions (wheels) for focalpy 0.0.3
File Actions
focalpy-0.0.3-cp312-abi3-win_amd64.whl CPython 3.12 abi3 Windows x86-64 Details for focalpy-0.0.3-cp312-abi3-win_amd64.whl
focalpy-0.0.3-cp312-abi3-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl CPython 3.12 abi3 Linux glibc 2.28+ x86-64, Linux glibc 2.27+ x86-64 Details for focalpy-0.0.3-cp312-abi3-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
focalpy-0.0.3-cp312-abi3-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl CPython 3.12 abi3 Linux glibc 2.28+ ARM64, Linux glibc 2.27+ ARM64 Details for focalpy-0.0.3-cp312-abi3-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl
focalpy-0.0.3-cp312-abi3-macosx_11_0_arm64.whl CPython 3.12 abi3 macOS 11.0+ ARM64 Details for focalpy-0.0.3-cp312-abi3-macosx_11_0_arm64.whl
focalpy-0.0.3-cp312-abi3-macosx_10_15_x86_64.whl CPython 3.12 abi3 macOS 10.15+ x86-64 Details for focalpy-0.0.3-cp312-abi3-macosx_10_15_x86_64.whl

Total release size: 1.7 MB

Release files / focalpy-0.0.3.tar.gz

Download URL focalpy-0.0.3.tar.gz
Size 261.3 kB
Tags Source
SHA-256 checksum
How to use checksums
596704d3aa80a8aefdcc9740f6f610785cc3e495bb75c93ba5d8c2d5f412690b
BLAKE2b-256 checksum
How to use checksums
74e9ea4a99c92166ee7a7a5bdc5cbb88eb595439bb5281d3a858be81ca9ef916
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 16, 2026.

Transparency log

Release files / focalpy-0.0.3-cp312-abi3-win_amd64.whl

Download URL focalpy-0.0.3-cp312-abi3-win_amd64.whl
Size 467.2 kB
Tags CPython 3.12 Windows x86-64 abi3
SHA-256 checksum
How to use checksums
196e1f80958a7778f426198746f323d995ad538f52ff86dabcfc86aa4a8077a8
BLAKE2b-256 checksum
How to use checksums
8a0587778004b1c6adda86443292044bb57ec0e84a50aad44066ccdb561a64cd
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 16, 2026.

Transparency log

Release files / focalpy-0.0.3-cp312-abi3-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl

Download URL focalpy-0.0.3-cp312-abi3-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Size 347.5 kB
Tags CPython 3.12 Linux glibc 2.27+ x86-64 Linux glibc 2.28+ x86-64 abi3
SHA-256 checksum
How to use checksums
6d72a898f70fc6eb3e2d6255f173a2bdf905556998976f1728091d3e322861e8
BLAKE2b-256 checksum
How to use checksums
7c15428e912862200e86f34cdf4d7aec8974ba189e922e36a09137b460d56797
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 16, 2026.

Transparency log

Release files / focalpy-0.0.3-cp312-abi3-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl

Download URL focalpy-0.0.3-cp312-abi3-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl
Size 314.6 kB
Tags CPython 3.12 Linux glibc 2.27+ ARM64 Linux glibc 2.28+ ARM64 abi3
SHA-256 checksum
How to use checksums
68de028b82fab60323f866ff6761e5824dfc68f913b080a551720c1b085dcde8
BLAKE2b-256 checksum
How to use checksums
5419974702b88982c67e70e82020f6f48c452534493edfc5b981b049d8e769d4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 16, 2026.

Transparency log

Release files / focalpy-0.0.3-cp312-abi3-macosx_11_0_arm64.whl

Download URL focalpy-0.0.3-cp312-abi3-macosx_11_0_arm64.whl
Size 162.1 kB
Tags CPython 3.12 abi3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
d1500a5c922e901466058fb245149ec56fb8dd2314bf4acfacfed40e95a81269
BLAKE2b-256 checksum
How to use checksums
cec89f7332895687cd4466cf0418f62acacde7a06e419e1a8254e8afc3f64bc2
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 16, 2026.

Transparency log

Release files / focalpy-0.0.3-cp312-abi3-macosx_10_15_x86_64.whl

Download URL focalpy-0.0.3-cp312-abi3-macosx_10_15_x86_64.whl
Size 179.7 kB
Tags CPython 3.12 abi3 macOS 10.15+ x86-64
SHA-256 checksum
How to use checksums
791df6042a473e959d8676a718d6fad10871a311167632c47913447822858aa8
BLAKE2b-256 checksum
How to use checksums
6ee1e00f3b06ca6acd9db394ba51a0b0783e62e6bea997e0788e80b5bbf92ae8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 16, 2026.

Transparency log

Security for focalpy 0.0.3

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 6 files in this release have been uploaded with provenance metadata.

Select from 6 files

Attestation for focalpy-0.0.3.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 16, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page