LibRaw
An Elixir library for native camera RAW decoding on the BEAM, powered by a Rustler NIF that wraps libraw.
Prerequisites
libraw must be installed before compiling or running :libraw:
# macOS
brew install libraw
# Debian / Ubuntu
apt install libraw-devLGPL compliance: libraw is dynamically linked at runtime. Distributing your application requires that end users can replace the libraw shared library. See the libraw license for details.
Installation
Add :libraw to your mix.exs dependencies:
def deps do
[
{:libraw, "~> 0.1"}
]
endUsage
Decode a RAW file
{:ok, image} = LibRaw.decode("/path/to/photo.CR3")
# => %{pixels: <<...>>, width: 6000, height: 4000, colors: 3, bps: 8}
# 16-bit output with linear gamma
{:ok, image16} = LibRaw.decode("/path/to/photo.CR3",
output_bps: 16,
gamma: :linear
)
# Custom gamma curve
{:ok, image_custom} = LibRaw.decode("/path/to/photo.NEF",
gamma: {2.4, 12.92},
use_camera_wb: true,
no_auto_bright: true
)Options
| Option | Type | Default | Description |
|---|---|---|---|
use_camera_wb | boolean | true | Use white balance stored in the file |
no_auto_bright | boolean | false | Disable automatic brightening |
output_bps | 8 | 16 | 8 | Bits per sample in the output |
gamma | :srgb | :linear | {g0, g1} | :srgb | Gamma curve |
Return value
%{
pixels: binary(), # raw pixel bytes (interleaved RGB or RGBA)
width: non_neg_integer(),
height: non_neg_integer(),
colors: non_neg_integer(), # number of color channels (typically 3)
bps: non_neg_integer() # bits per sample of the output
}Read metadata without decoding
{:ok, meta} = LibRaw.metadata("/path/to/photo.CR3")
# => %{
# camera_make: "Canon",
# camera_model: "EOS R5",
# captured_at: ~U[2023-06-15 10:32:11Z], # DateTime UTC, or nil
# iso: 400.0,
# shutter: 0.002, # seconds
# aperture: 2.8, # f-number
# orientation: 0 # EXIF flip code
# }Architecture
lib/
lib_raw.ex Public API: decode/2, metadata/1, gamma resolution, timestamp parsing
lib_raw/
nif.ex use Rustler + NIF stubs (nif_not_loaded fallbacks)
native/
libraw_nif/
Cargo.toml deps: rustler = "0.33"; build-deps: cc = "1", pkg-config = "0.3"
build.rs pkg_config::probe("libraw") for dynamic linking; cc::Build compiles wrapper.c
src/
lib.rs rustler::init! and two #[rustler::nif(schedule = "DirtyCpu")] functions
wrapper.c thin C shim — C compiler resolves all struct field offsets
raw.rs safe Rust RAII wrappers around libraw_data_t / libraw_processed_image_t
error.rs LibRawError enum + helpersWhy a C shim?
Direct bindgen / libraw-sys approaches embed struct field offsets at compile
time, which can break across libraw versions 0.20, 0.21, and 0.22 as the
struct layout evolves. wrapper.c is compiled with the same headers as the
installed libraw, so the C compiler always uses the correct offsets. Rust
calls only opaque C functions and never touches libraw structs directly.
Dirty CPU Schedulers
Both NIFs (decode_nif and metadata_nif) are annotated with
schedule = "DirtyCpu". Decoding a RAW file typically takes 100–500 ms,
which is far beyond the 1 ms NIF time budget for normal schedulers. Running
on dirty schedulers prevents blocking the BEAM scheduler threads.
License
MIT — see LICENSE.