Read a DNG's NoiseProfile
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.
This commit is contained in:
@@ -32,6 +32,10 @@ fn main() {
|
||||
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) => {
|
||||
|
||||
@@ -544,6 +544,17 @@ pub(crate) fn parse_exif_offset(s: &str) -> Option<i32> {
|
||||
Some(sign * (h * 60 + m))
|
||||
}
|
||||
|
||||
/// TRACES: FR-DEV-3g
|
||||
/// The DNG `NoiseProfile` of a file, if it carries one: `(S, O)` per CFA
|
||||
/// colour plane, variance `S·x + O` in black-to-white normalised units. See
|
||||
/// [`profile::read_noise_profile`]. Reads the header, not the image.
|
||||
pub fn noise_profile(bytes: &[u8]) -> Option<Vec<(f32, f32)>> {
|
||||
use rawler::rawsource::RawSource;
|
||||
let source = RawSource::new_from_slice(bytes);
|
||||
let decoder = rawler::get_decoder(&source).ok()?;
|
||||
profile::read_noise_profile(decoder.as_ref())
|
||||
}
|
||||
|
||||
/// TRACES: FR-RAW-3 | FR-EXP-9
|
||||
/// Fully decode sensor data.
|
||||
///
|
||||
|
||||
@@ -740,6 +740,35 @@ pub fn read_dng_matrices(decoder: &dyn rawler::decoders::Decoder) -> DngMatrices
|
||||
}
|
||||
}
|
||||
|
||||
/// TRACES: FR-DEV-3g
|
||||
/// The DNG `NoiseProfile` tag (51041): the converter's measured noise for
|
||||
/// this body at this ISO, as `(S, O)` per CFA colour plane, so that a
|
||||
/// photosite's variance is `S·x + O` with `x` normalised black-to-white.
|
||||
///
|
||||
/// One pair means all planes share it. `None` where the file has no such
|
||||
/// tag — every proprietary raw, and DNGs from converters that do not measure
|
||||
/// — or where a value is not a finite non-negative number. The learned
|
||||
/// denoise's second-best noise source (denoise.md §3.3), after a measured
|
||||
/// table for the body.
|
||||
pub fn read_noise_profile(decoder: &dyn rawler::decoders::Decoder) -> Option<Vec<(f32, f32)>> {
|
||||
use rawler::decoders::WellKnownIFD;
|
||||
use rawler::tags::DngTag;
|
||||
|
||||
let ifd = decoder.ifd(WellKnownIFD::Root).ok()??;
|
||||
let entry = ifd.get_entry_recursive(DngTag::NoiseProfile)?;
|
||||
let n = entry.count() as usize;
|
||||
if n < 2 || n % 2 != 0 {
|
||||
return None;
|
||||
}
|
||||
let pairs: Vec<(f32, f32)> = (0..n / 2)
|
||||
.map(|i| (entry.force_f32(2 * i), entry.force_f32(2 * i + 1)))
|
||||
.collect();
|
||||
pairs
|
||||
.iter()
|
||||
.all(|(s, o)| s.is_finite() && o.is_finite() && *s >= 0.0 && *o >= 0.0)
|
||||
.then_some(pairs)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
Reference in New Issue
Block a user