Metadata-Version: 2.1
Name: pyavif
Version: 0.0.2
Summary: AVIF bindings for Python with NumPy support
Author-Email: Andrey Volodin <andrey@gracia.ai>
License: GNU General Public License v3 (GPLv3)
Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
Project-URL: Homepage, https://github.com/gracia-labs/pyavif
Requires-Python: >=3.12
Requires-Dist: numpy>=1.20.0
Provides-Extra: test
Requires-Dist: pytest>=6.0; extra == "test"
Requires-Dist: pytest-cov>=2.10; extra == "test"
Provides-Extra: dev
Requires-Dist: pytest>=6.0; extra == "dev"
Requires-Dist: pytest-cov>=2.10; extra == "dev"
Requires-Dist: ipython; extra == "dev"
Requires-Dist: jupyter; extra == "dev"
Requires-Dist: matplotlib; extra == "dev"
Description-Content-Type: text/markdown

# pyavif

> **For LLM agents:** This README is the primary context source for the project.
> It covers installation, full API surface, and usage patterns. Build details are
> in [`docs/`](./docs/).

Opinionated, easy to use and performance-oriented AVIF encoder/decoder for Python. Built on
[libavif](https://github.com/AOMediaCodec/libavif) with
[nanobind](https://github.com/wjakob/nanobind) for minimal overhead.

**Highlights:**
- Decode (DAV1D, AOM) and encode (AOM, RAV1E) with full codec choice
- 8/10/12-bit, RGB/RGBA, animated AVIF
- Batch encode/decode with parallel workers
- ICC, EXIF, XMP metadata support
- GIL released during all C++ operations
- Zero-copy `to_torch()` helper
- Prebuilt wheels for Linux x86_64, macOS arm64, Windows x86_64 (Python 3.12+)

## Installation

```bash
pip install pyavif
```

## Quick Start

### Decode

```python
from pyavif import Decoder

decoder = Decoder()
decoder.init("image.avif")
image = decoder.get_image(0)  # numpy ndarray (H, W, C), uint8 or uint16
```

### Encode

```python
import numpy as np
from pyavif import Encoder

encoder = Encoder("out.avif", width=256, height=256, channels=3, depth=8)
encoder.add_frame(np.zeros((256, 256, 3), dtype=np.uint8))
encoder.finish()
```

## API Reference

### `Decoder`

```
init(filepath, decoder_threads=1, codec=DecoderCodec.DAV1D)
get_image(index, force_rgba=False) -> ndarray   # random access by frame index
next_image(force_rgba=False) -> ndarray          # sequential access
get_image_count() -> int
get_width() / get_height() / get_depth() -> int
has_alpha() -> bool
get_pixel_format() -> PixelFormat
```

### `BatchDecoder`

```
BatchDecoder(file_names, max_workers=0, decoder_threads=1,
             force_rgba=False, codec=DecoderCodec.DAV1D)
next_batch() -> (int, dict[str, ndarray])       # frame_index, {path: image}
get_batch_at(frame_idx) -> (int, dict[str, ndarray])
files() -> list[str]
get_image_count() -> int
```

### `Encoder`

```
Encoder(output_path, width, height, channels, depth, options=EncoderOptions())
add_frame(ndarray, duration=1, quality_override=None, quality_alpha_override=None)
finish()
set_icc(data: bytes) / set_exif(data: bytes) / set_xmp(data: bytes)
add_advanced_option(key: str, value: str)
```

### `BatchEncoder`

```
BatchEncoder(output_paths, options=EncoderOptions())
add_image_batch(images, duration=1, depth=None)  # depth is required for uint16 (10 or 12)
finish_all()
files() -> list[str]
```

### `EncoderOptions`

| Property | Type | Default |
|---|---|---|
| `quality` | int | 80 |
| `quality_alpha` | int | 100 (lossless) |
| `speed` | int | AVIF_SPEED_DEFAULT |
| `max_threads` | int | 1 |
| `codec` | `EncoderCodec` | AOM |
| `pixel_format` | `PixelFormat` | YUV444 |
| `range` | `Range` | FULL |
| `timescale` | int | 30 |
| `keyframe_interval` | int | 0 |
| `repetition_count` | int | infinite |
| `auto_tiling` | bool | True |
| `tile_rows_log2` | int | 0 |
| `tile_cols_log2` | int | 0 |
| `alpha_premultiplied` | bool | False |

### Enums

- **`DecoderCodec`**: `DAV1D`, `AOM`
- **`EncoderCodec`**: `RAV1E`, `AOM`
- **`PixelFormat`**: `YUV444`, `YUV422`, `YUV420`, `YUV400`
- **`Range`**: `FULL`, `LIMITED`

### `to_torch(array, *, layout="channels_last", pin_memory=False)`

Zero-copy NumPy-to-PyTorch conversion. `layout="chw"` returns a `CxHxW` view.

## License

GPLv3. See [`LICENSE`](./LICENSE).
