Files
lightcraft/docs/cr3.md
T
Claude 69f117632b Merge PR #754
# Conflicts:
#	crates/engine/src/media.rs
2026-10-11 11:39:46 +00:00

6.6 KiB
Raw Blame History

Canon CR3 decoding

The CR3 adapter reads the CRX Bayer track, CMP1 compression descriptor, CDI1/IAD1 sensor geometry and timed CTMD maker notes. White balance, black/white levels and the default crop come from the file. A Dual Pixel delta track is never substituted for the primary Bayer track.

The in-camera aspect ratio (3:2, 4:3, 16:9, 1:1) is not applied to the sensor data. The maker note's AspectInfo (tag 0x009a: aspect code, cropped width, height, left, top) gives the shot's rectangle inside the recommended crop, and the decoder uses it as the default crop, so the developed image has the shape the photographer framed (PowerShot SX70 HS, EOS 250D, PowerShot G5 X Mark II, EOS M6 Mark II and EOS M50 samples). A rectangle that is missing or does not fit inside the crop is ignored. The embedded JPEG still shows the whole frame.

The EOS R5 Mark II's APS-C crop-mode files (5376 x 3574) state an active area that starts in dark columns (132..~256 sit at the black level) and stops ~130 columns short of the image, while their recommended crop (5088 x 3392, the camera JPEG's size) lies on the image. When the recommended crop doesn't fit inside the stated active area, it is used as the valid area: on raw.pixls.us 7883/7884 the render matches the camera JPEG (high-pass correlation 0.99, was -0.13 with a black band on the left). No other of the 110 CC0 CR3s changes.

The decoder is independent Rust code shared by desktop, CLI and WebAssembly; it has no operating-system imaging dependency or external decoder. Its sources are the public prose descriptions and patents cited in crates/raw/src/vendor/crx.rs. Unsupported coding variants retain the existing embedded-JPEG fallback and its visible warning when a JPEG is present. So does any other CR3 decode error (a corrupt file, or a body whose CRX stream the decoder misreads): every CR3 opened from its embedded JPEG before this decoder, and only three bodies are verified, so a CR3 never fails to import or load where a preview exists. HEVC-only preview tracks are recognized but are not decoded.

Verified sensor decoding

CRX version 0x100, four Bayer planes and ff01/ff02/ff03 marker headers support lossless RAW and three-level C-RAW with horizontal tiles. Uniform quantization values 4–43 are independently verified; 44 continues the sequence (step 102, checked against a measured step on an EOS R50 C-RAW file). Version 0x200 C-RAW supports the ff11/ff12/ff13 marker family, a single 14-bit Bayer tile and its adaptive quantization map. C-RAW is quantized, so next to clipped highlights its reconstruction can overshoot the sensor range by a few quantization steps (up to 36 codes above 16383 in 17 CC0 files); those samples are clamped to the CMP1 bit depth. A lossless sample outside the bit depth is still corrupt data. Other unverified coding variants return explicit unsupported errors. Header probing validates the same coding variants and tile/plane layout as full decoding.

crates/raw/tests/cr3_corpus.rs checks every full-sensor sample against a black-box reference, before cropping or colour processing. Public CC0 files are downloaded by cargo xtask corpus --download and kept outside version control. The exact inputs and reference hashes are:

File Input SHA-256 Sensor FNV-1a (u16 little-endian)
Canon M50 RAW, raw.pixls.us 4657 1ba9ad6b51b315b88820eb0fe5fd7f52bc136ba37b1a5ca76825ebfe25b18058 62261f0ba81cfcd2
Canon M50 C-RAW, raw.pixls.us 2663 15384b775867ec4c42b11882837f1e368cedc0561832ffab271221e6bb80be4c 9aacbfe66f505e1b
Canon R100 RAW, raw.pixls.us 7896 6d83217d58a5e6d2dabcf470430e91a16676b60531c025b81fa7453fc74b0521 cbe5299ab9c52630
Canon R100 C-RAW, raw.pixls.us 7897 0b66842b2fe00329ebc05fe8ab9357ddbbe1a0a2d2e69a2893102de7fdf5c942 341c706b37c38bbf
Canon R8 RAW, raw.pixls.us 6585 7d5c6dbb11ff7e6ee58715d90a20f5d801e8b103f4711ff6ea329ceafcef254c 63c1c6d8d1eb2312
Canon R8 C-RAW, raw.pixls.us 6587 df33cf394573645ce03dea1b2e9f0b5cc2b7734e9e3391ee7114151414cf2812 f836a663a795a375

Reference pixels were observed from the DNGLab 0.8.0 macOS arm64 release binary; no decoder source was read or copied, and it is not shipped or linked. The release ZIP SHA-256 was ee70805cb60f18d5ed62548c4e595f6cee0a33407e15358eb1766a46afc1c16e. All six sensor comparisons pass. R8 HEVC preview tracks are distinguished from the primary Bayer RAW; the HEVC preview itself is not decoded. Missing corpus files are reported as skipped by the tests.

The version 0x200 QP predictor and entropy framing were checked independently. Controlled input mutations establish floor averaging of paired QP rows before the nonlinear integer table, then multiplication, division by eight, addition of the subband base and a minimum step of one. QP values 131–167 are verified; unmeasured values are rejected.

An odd QP-map height (EOS R7: 4732 sensor rows give 1183 QP rows) leaves the last row of the second-level high-frequency bands without a partner row; that row's QP is used on its own. The odd-height map decodes to the exact end of its entropy stream. This rule is not yet checked against a black-box reference: it was checked on paired R7 RAW and C-RAW captures of the same scene (ISO 100 and 6400), whose developed images match visually and whose sensor means agree within 0.1% at ISO 6400. Files with even-height maps never reach this rule, so the six verified fixtures are unchanged. Partial-subband and rounded-plane coding, vertical C-RAW tiling and adaptive low-frequency gains remain unsupported. The decoder bounds image dimensions, sample count, tile count, marker headers and temporary allocations. The full sensor buffer is allocated only after the first plane decodes, and plane buffers grow row by row, so a small crafted file fails before reserving memory for the image it claims.

Colour limits

CR3 carries as-shot white balance but no measured camera matrix is imported here. The existing guarded embedded-JPEG fitting path now includes CR3, with both proxies oriented identically before gathering training pairs. A bounded crop/translation registration of the JPEG is confirmed on held-out edge directions before fitting colour; the existing held-out colour acceptance gates are unchanged. No lens distortion correction is applied (LightCraft has no lens profiles of its own yet), so lenses whose camera JPEG is strongly distortion-corrected may fail the gates and keep the neutral matrix. Render cache version 13 invalidates earlier thumbnails. All developed pixels still come from the RAW sensor; a rejected colour fit retains the documented neutral matrix fallback. See camera-preview-colour.md for the limits of this estimate.