fovea 0.5.0

A high-precision, type-safe computer vision library guaranteeing absolute image correctness at compile time
Documentation
# Camera buffers


Camera SDKs usually hand you bytes plus metadata: width, height, stride, bit depth, channel order, endian-ness, and sometimes padding. fovea's job is to turn that boundary into a typed image as early as possible.

## 8-bit packed buffers


For alignment-1 pixel types such as `Mono8`, `Srgb8`, `Rgb8`, and `Bgr8`, `Image::from_raw_bytes` can take ownership of the byte allocation without copying.

```rust
use fovea::image::{Image, ImageView};
use fovea::pixel::Mono8;

let raw = vec![0u8, 64, 128, 255];
let img: Image<Mono8> = Image::from_raw_bytes(2, 2, raw)?;

assert_eq!(img.width(), 2);
assert_eq!(img.pixel_at(1, 1), Mono8::new(255));
# Ok::<(), fovea::Error>(())

```

Use the pixel type that matches the buffer. If the SDK says BGR, use `Bgr8`, not `Rgb8`.

## Aligned pixels: copy into owned storage


For pixel types whose alignment is greater than 1, such as `Mono16`, use `Image::from_bytes_copy`. It copies bytes into an allocation with the correct alignment.

```rust
use fovea::image::{Image, ImageView};
use fovea::pixel::Mono16;

let bytes = [0u8, 1, 0, 2];
let img: Image<Mono16> = Image::from_bytes_copy(2, 1, &bytes)?;

assert_eq!(img.size(), fovea::Size::new(2, 1));
# Ok::<(), fovea::Error>(())

```

Be explicit about endian-ness at the camera boundary. `from_bytes_copy` uses the pixel's native byte interpretation; if your SDK delivers a fixed endian format, normalize the bytes before constructing the image or use the endian helpers on `PlainPixel` for individual pixels.

## Borrowing existing storage


If another owner controls the allocation lifetime, borrow it with `ImageRef` or `ImageRefMut` rather than taking ownership.

```rust
use fovea::image::{ImageRef, ImageView};
use fovea::pixel::Mono8;

let sdk_pixels = [Mono8::new(10), Mono8::new(20), Mono8::new(30), Mono8::new(40)];
let view = ImageRef::new(2, 2, &sdk_pixels)?;

assert_eq!(view.pixel_at(0, 1), Mono8::new(30));
# Ok::<(), fovea::Error>(())

```

For a padded or strided SDK buffer, build a borrowed strided view if the storage is already typed, or copy row-by-row into an owned `Image<P>` if the source is only bytes.

## Reinterpreting byte slices


`PlainPixel::cast_slice` is useful when you need a borrowed pixel slice over raw bytes. It checks length and alignment before returning a typed slice.

```rust
use fovea::pixel::{Mono8, PlainPixel};

let raw = [1u8, 2, 3, 4];
let pixels: &[Mono8] = Mono8::cast_slice(&raw).unwrap();

assert_eq!(pixels.len(), 4);
assert_eq!(pixels[2], Mono8::new(3));
```

If `cast_slice` returns `None`, do not force it with `unsafe`. The buffer length, alignment, or pixel type is wrong for zero-copy reinterpretation.

This is also how a raw Bayer frame enters the type system. The CFA types are `#[repr(transparent)]` over their sample, so a camera buffer reported as `BayerRG8` needs no copy — only the right type name:

```rust
use fovea::pixel::PlainPixel;
use fovea::pixel::bayer::BayerRggb8;

let raw = [1u8, 2, 3, 4];
let pixels: &[BayerRggb8] = BayerRggb8::cast_slice(&raw).unwrap();

assert_eq!(pixels[2].value(), 3);
```

Match the SDK's format string to the type once, at the boundary, and everything downstream knows the pattern and the depth. Note that SFNC abbreviates the tile to its first row: `BayerRG8` is `BayerRggb8`, `BayerGB12` is `BayerGbrg12`.

From there the pipeline is two named steps — the camera's balance ratios on the mosaic, then interpolation to RGB at the same depth:

```rust
use fovea::image::Image;
use fovea::pixel::{Rgb12, bayer::BayerRggb12};
use fovea::transform::{BayerGains, MalvarHeCutler, demosaic, white_balance};

let raw = Image::fill(64, 48, BayerRggb12::new(2048));

let balanced = white_balance(&raw, BayerGains::new(1.9, 1.0, 1.6).unwrap());
let rgb: Image<Rgb12> = demosaic(&balanced, MalvarHeCutler);
```

The output type follows from the sample type (`BayerRggb12` → `Rgb12`), so a twelve-bit frame cannot silently become eight-bit here. Neither function takes a border policy: only reflection without edge duplication preserves a CFA sample's colour at the frame edge, so that treatment is pinned into the contract.

## Common mistakes


- **Treating BGR as RGB.** Use `Bgr8` / `Bgr16` at the boundary, then convert with `ColorSwap` only when you mean to.
- **Ignoring stride.** A region in a padded frame is not always contiguous. Use row access or a strided view.
- **Using `Srgb8` for linear camera data.** sRGB means a transfer function. Most raw camera data is linear mono or linear RGB/BGR.
- **Inventing runtime flags for layout.** Prefer distinct pixel types. The type should say what the bytes mean.
- **Typing a CFA frame as `Mono<N>`.** It loses the mosaic pattern, and with it every guard against blurring, resizing, or odd-origin-cropping across colour channels. Use the `bayer` types.