Metadata-Version: 2.4
Name: focalpy
Version: 0.0.1
Summary: Image processing in C++ with a zero-copy NumPy interface
Keywords: image-processing,imaging,computer-vision,numpy,cpp
Author-Email: Edison Sun <edisonsun31@gmail.com>
License-Expression: MIT
License-File: LICENSE
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Science/Research
Classifier: Programming Language :: C++
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Multimedia :: Graphics
Classifier: Topic :: Scientific/Engineering :: Image Processing
Project-URL: Homepage, https://github.com/edisons3608/focal
Project-URL: Repository, https://github.com/edisons3608/focal
Project-URL: Issues, https://github.com/edisons3608/focal/issues
Requires-Python: >=3.12
Requires-Dist: numpy>=1.23
Provides-Extra: test
Requires-Dist: pytest; extra == "test"
Requires-Dist: scipy; extra == "test"
Requires-Dist: scikit-image; extra == "test"
Requires-Dist: pillow; extra == "test"
Description-Content-Type: text/markdown

# focal

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

```python
import focal

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

## 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.

**Nothing converts silently.** Array arguments are declared `.noconvert()`, so
passing a float64 or non-contiguous array raises `TypeError` instead of quietly
costing a full-size copy. Converting is the caller's decision.

**Filtering happens in linear light.** `imread` decodes sRGB to linear float32
and `imwrite` encodes back. Blurring or resizing gamma-encoded values darkens
the result — on a synthetic test chart, a σ=3 blur done in gamma space came out
29% darker than the same blur done correctly, with individual pixels off by
74/255.

## What's here

| | |
|---|---|
| I/O | `imread`, `imwrite` — PNG, JPEG, BMP, TGA via stb |
| Point ops | `gamma`, `exposure`, `brightness_contrast`, `invert`, `clip`, `blend`, `apply_lut` |
| Filtering | `gaussian_blur` (separable) |
| Sampling | `sample` — nearest and bilinear, border-aware |
| Color | `srgb_to_linear`, `linear_to_srgb` |

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

## Install

```bash
pip install focalpy
```

Building from source needs a C++20 compiler and CMake:

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

## Status

Early. The API will change. Tests compare against `scipy.ndimage` and `PIL`
where an oracle exists.

## License

MIT. Bundles [stb](https://github.com/nothings/stb) (public domain).
