Find hot photosites without repairing them, and measure a sensor's aging
The hot-pixel pass could only repair: it returned how many photosites it changed and threw away which. find_hot_pixels runs the same pass and returns them as sensor coordinates, leaving the frame alone, so a sensor's defects can be tracked across frames. sensor_scan prints each frame's candidates, and with --probe reads a list of coordinates back out of every frame. Run over 53 6D raws from 2015 to 2026, it found 32 persistent defects, 2 in 2015 and 32 by 2026, and showed what a defect map has to account for: a frame that does not flag a photosite proves nothing unless its neighbourhood is dark, and the 6D hides some of its defects itself above ISO 5000. docs/dev/sensor-health.md records the findings and the design they argue for.
This commit is contained in:
@@ -7,7 +7,7 @@
|
||||
//! see of a defect it missed is the coloured cross the demosaic makes of it.
|
||||
|
||||
use dr_decode::{CfaPattern, CropRect, RawImage};
|
||||
use dr_gpu::{AdjustPass, Demosaicer, GpuContext};
|
||||
use dr_gpu::{AdjustPass, Demosaicer, GpuContext, Photosite};
|
||||
use dr_pipeline::EditGraph;
|
||||
|
||||
const SIZE: u32 = 36;
|
||||
@@ -171,3 +171,43 @@ fn the_repaired_mosaic_reads_back_with_only_the_defect_changed() {
|
||||
let others = (0..raw.data.len()).filter(|&i| i != at);
|
||||
assert!(others.into_iter().all(|i| raw.data[i] == before.data[i]));
|
||||
}
|
||||
|
||||
/// Finding without repairing (docs/dev/sensor-health.md): the same verdict as
|
||||
/// the repair, as sensor coordinates, with the frame left as it was. The
|
||||
/// sensor health record builds on this, so it must name exactly the
|
||||
/// photosites the repair would change — the hot one and the dead one, and
|
||||
/// not the star.
|
||||
#[test]
|
||||
fn finding_names_what_the_repair_would_change_and_changes_nothing() {
|
||||
let Some(ctx) = ctx() else {
|
||||
eprintln!("no GPU adapter; skipping");
|
||||
return;
|
||||
};
|
||||
let d = Demosaicer::new(&ctx).expect("demosaicer");
|
||||
let mut set = vec![(MIDDLE, MIDDLE, WHITE), (9, 25, 0)];
|
||||
for dy in 0..3 {
|
||||
for dx in 0..3 {
|
||||
set.push((4 + dx, 4 + dy, WHITE));
|
||||
}
|
||||
}
|
||||
let raw = frame(CfaPattern::Rggb, 1600, &set);
|
||||
let mut found = d.find_hot_pixels(&raw).expect("find");
|
||||
found.sort_by_key(|p| (p.y, p.x));
|
||||
assert_eq!(
|
||||
found,
|
||||
vec![
|
||||
Photosite {
|
||||
x: MIDDLE,
|
||||
y: MIDDLE,
|
||||
hot: true
|
||||
},
|
||||
Photosite {
|
||||
x: 9,
|
||||
y: 25,
|
||||
hot: false
|
||||
},
|
||||
]
|
||||
);
|
||||
let mut repaired = raw.clone();
|
||||
assert_eq!(d.repair_hot_pixels(&mut repaired).expect("repair"), 2);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user