Camera RAW and DNG decoder in safe Rust. Scene-referred linear f32 output by default, sRGB u8 opt-in. Three swappable backends for different camera coverage vs. dependency tradeoffs.
use zenraw::{decode, RawDecodeConfig};
use enough::Unstoppable;
let data: &[u8] = &[/* RAW file bytes */];
let output = decode(data, &RawDecodeConfig::default(), &Unstoppable)?;
println!("{}x{} {} {}", output.info.width, output.info.height,
output.info.make, output.info.model);
// output.pixels is a PixelBuffer<RGBF32_LINEAR>For display-referred sRGB u8 output:
let config = RawDecodeConfig::default().with_gamma(true);
let output = decode(data, &config, &Unstoppable)?;
// output.pixels is a PixelBuffer<RGB8_SRGB>Cancellation and deadlines use enough::Stop — pass
&Unstoppable when you don't need either.
- Parse camera RAW/DNG file (via rawloader or rawler)
- Normalize sensor values using per-channel black/white levels
- Demosaic Bayer CFA → RGB (Malvar-He-Cutler by default, bilinear available)
- Demosaic X-Trans 6×6 CFA → RGB (bilinear, rawler backend only)
- Apply white balance coefficients
- Apply camera → XYZ → sRGB color matrix
- Crop to active area (from camera metadata)
- Apply EXIF orientation (rotation/flip)
- Optionally apply sRGB gamma curve
| Backend | Feature | Cameras | Formats | Notes |
|---|---|---|---|---|
| rawloader | rawloader (default) |
~200 | Bayer only | Lightweight, LGPL-2.1 |
| rawler | rawler |
~300+ | Bayer + X-Trans, CR3, JXL DNG | Broader support, LGPL-2.1 |
| darktable | darktable |
900+ | Everything darktable handles | Shells out to darktable-cli |
When both rawloader and rawler are enabled, rawler takes priority.
The darktable backend is independent — it delegates the entire pipeline to
darktable-cli and returns its processed output.
| Format | Extensions | Backend |
|---|---|---|
| Adobe DNG | .dng |
rawloader, rawler |
| Canon | .cr2, .cr3 |
rawloader (CR2), rawler (CR2+CR3) |
| Nikon | .nef, .nrw |
rawloader, rawler |
| Sony | .arw, .srf, .sr2 |
rawloader, rawler |
| Fujifilm | .raf |
rawler (X-Trans + Bayer) |
| Panasonic/Leica | .rw2 |
rawloader, rawler |
| Pentax | .pef |
rawloader, rawler |
| Olympus | .orf |
rawloader, rawler |
| Hasselblad | .3fr |
rawloader, rawler |
| Phase One | .iiq |
rawloader, rawler |
| Epson | .erf |
rawloader, rawler |
Plus many more via rawler. Detection works on file content, not extension.
| Feature | Default | Description |
|---|---|---|
std |
yes | Enable std (required for darktable, rawler) |
rawloader |
yes | rawloader decode backend |
ultrahdr |
yes | UltraHDR gain map support via ultrahdr-core |
rawler |
no | rawler decode backend (broader camera support) |
darktable |
no | darktable-cli backend (requires darktable installed) |
exif |
no | EXIF metadata extraction via kamadak-exif |
xmp |
no | XMP metadata extraction |
apple |
no | Apple APPLEDNG/AMPF metadata (implies exif + xmp) |
zencodec |
no | zencodec trait integration (DecoderConfig, ImageInfo) |
use zenraw::{RawDecodeConfig, DemosaicMethod};
let config = RawDecodeConfig {
demosaic: DemosaicMethod::MalvarHeCutler, // or Bilinear
apply_gamma: false, // true → sRGB u8, false → linear f32
apply_crop: true, // use camera's crop/active area
apply_orientation: true, // apply EXIF rotation/flip
max_pixels: 300_000_000, // reject images above this
..Default::default()
};With the zencodec feature, zenraw implements DecoderConfig for use in
format-agnostic decode pipelines. Two format definitions are exported:
DNG_FORMAT and RAW_FORMAT.
use zenraw::RawDecoderConfig;
use zencodec::decode::DecoderConfig;
let config = RawDecoderConfig::new();
let job = config.job();
let info = job.probe(data)?;
println!("{}x{}, orientation={}", info.width, info.height, info.orientation());The zencodec integration populates ImageInfo with orientation, bit depth,
and XMP metadata (when the xmp feature is enabled).
Developed with Claude (Anthropic). Not all code manually reviewed. Review critical paths before production use.
| State of the art codecs* | zenjpeg · zenpng · zenwebp · zengif · zenavif (rav1d-safe · zenrav1e · zenavif-parse · zenavif-serialize) · zenjxl (jxl-encoder · zenjxl-decoder) · zentiff · zenbitmaps · heic · zenraw · zenpdf · ultrahdr · mozjpeg-rs · webpx |
| Compression | zenflate · zenzop |
| Processing | zenresize · zenfilters · zenquant · zenblend |
| Metrics | zensim · fast-ssim2 · butteraugli · resamplescope-rs · codec-eval · codec-corpus |
| Pixel types & color | zenpixels · zenpixels-convert · linear-srgb · garb |
| Pipeline | zenpipe · zencodec · zencodecs · zenlayout · zennode |
| ImageResizer | ImageResizer (C#) — 24M+ NuGet downloads across all packages |
| Imageflow | Image optimization engine (Rust) — .NET · node · go — 9M+ NuGet downloads across all packages |
| Imageflow Server | The fast, safe image server (Rust+C#) — 552K+ NuGet downloads, deployed by Fortune 500s and major brands |
* as of 2026
archmage · magetypes · enough · whereat · zenbench · cargo-copter
And other projects · GitHub @imazen · GitHub @lilith · lib.rs/~lilith · NuGet (over 30 million downloads / 87 packages)
Dual-licensed: AGPL-3.0 or commercial.
I've maintained and developed open-source image server software — and the 40+ library ecosystem it depends on — full-time since 2011. Fifteen years of continual maintenance, backwards compatibility, support, and the (very rare) security patch. That kind of stability requires sustainable funding, and dual-licensing is how we make it work without venture capital or rug-pulls. Support sustainable and secure software; swap patch tuesday for patch leap-year.
Your options:
- Startup license — $1 if your company has under $1M revenue and fewer than 5 employees. Get a key →
- Commercial subscription — Governed by the Imazen Site-wide Subscription License v1.1 or later. Apache 2.0-like terms, no source-sharing requirement. Sliding scale by company size. Pricing & 60-day free trial →
- AGPL v3 — Free and open. Share your source if you distribute.
See LICENSE-COMMERCIAL for details.