The converter's measured noise for the body at that ISO, (S, O) per CFA plane: the learned denoise's best source for a body with no table of its own (denoise.md §3.3). Read from the header beside the colour tags, and printed by rawinfo. Checked against tifffile on a 6D DNG at ISO 5000: all six values agree.
66 lines
2.4 KiB
Rust
66 lines
2.4 KiB
Rust
//! Print what `decode` extracts from a RAW file.
|
||
//!
|
||
//! A sanity check on the pipeline's inputs: black and white levels, the CFA
|
||
//! pattern after re-phasing, as-shot white balance, and the camera→sRGB
|
||
//! matrix. Wrong values here produce a wrong image no shader can fix, so it
|
||
//! is worth being able to see them directly.
|
||
//!
|
||
//! ```sh
|
||
//! cargo run -p dr-decode --example rawinfo -- IMG.CR2
|
||
//! ```
|
||
|
||
fn main() {
|
||
let Some(path) = std::env::args().nth(1) else {
|
||
eprintln!("usage: rawinfo <file.cr2>");
|
||
std::process::exit(2);
|
||
};
|
||
|
||
let bytes = std::fs::read(&path).expect("read file");
|
||
let raw = dr_decode::decode(&bytes).expect("decode");
|
||
|
||
println!("file {path}");
|
||
println!("readout {} × {}", raw.width, raw.height);
|
||
println!(
|
||
"crop {} × {} at ({}, {})",
|
||
raw.crop.width, raw.crop.height, raw.crop.x, raw.crop.y
|
||
);
|
||
let (dx, dy) = raw.crop.shifts_cfa_phase();
|
||
println!(
|
||
"cfa {:?} (rephased: {dx}, {dy})",
|
||
raw.cfa_pattern
|
||
);
|
||
println!("black {:?}", raw.black_level);
|
||
println!("white {}", raw.white_level);
|
||
println!("wb_coeffs {:?}", raw.wb_coeffs);
|
||
match dr_decode::noise_profile(&bytes) {
|
||
Some(p) => println!("noise profile {p:?} ((S, O) per plane)"),
|
||
None => println!("noise profile none"),
|
||
}
|
||
|
||
match raw.color_matrix {
|
||
Some(m) => {
|
||
println!("cam→srgb");
|
||
for row in m.chunks(3) {
|
||
println!(
|
||
" [{:>8.4} {:>8.4} {:>8.4}]",
|
||
row[0], row[1], row[2]
|
||
);
|
||
}
|
||
// Each row should sum to roughly 1: a neutral camera-space colour
|
||
// must stay neutral in sRGB. Far from 1 means the normalisation
|
||
// or the matrix composition is wrong.
|
||
let sums: Vec<f32> = m.chunks(3).map(|r| r.iter().sum()).collect();
|
||
println!("row sums {sums:.4?} (≈1.0 each if correct)");
|
||
}
|
||
None => println!("cam→srgb none — uncalibrated body"),
|
||
}
|
||
|
||
// Sample the actual data range, which reveals a black-level or bit-depth
|
||
// mistake faster than any amount of staring at metadata.
|
||
let (min, max) = raw
|
||
.data
|
||
.iter()
|
||
.fold((u16::MAX, 0u16), |(lo, hi), &v| (lo.min(v), hi.max(v)));
|
||
println!("sample range {min} … {max}");
|
||
}
|