PRNU, DSNU, and hot pixels

This commit is contained in:
connor committed 2026-10-16 15:59:20 -04:00
1 parent 1ed95982ad
commit 3d0b129582
17 files changed
+763 -37

No files matched your search

+2 -1
View File
@@ -60,7 +60,7 @@ struct RenderArgs {
/// Camera fidelity tier: C0, or C1 (adds pixels that collect light over their area,
/// for graded edges and continuous thin parts, lens distortion, lens blur, and the
/// Sun, Moon and Earth where light time and aberration make them appear, detector
/// noise, and at lighting L1 stars).
/// noise and fixed pattern, and at lighting L1 stars).
#[arg(long, default_value = "C0")]
camera: CameraTier,
/// From camera C1: render each pixel as N x N finer ones (1 to 4; default 2), which
@@ -229,6 +229,7 @@ fn render(args: &RenderArgs) -> Result<(), String> {
supersampling: args.supersampling,
stars,
noise_seed: args.seed,
fixed_pattern: None,
};
let start = Instant::now();
let mut renderer = Renderer::new(camera, options).map_err(|e| e.to_string())?;
+21 -1
View File
@@ -74,6 +74,14 @@ pub struct CameraFile {
pub dark_current: Option<f64>,
/// From camera C1: whether the electrons collected carry shot noise.
pub shot_noise: Option<bool>,
/// From camera C1: photo-response nonuniformity, a fraction RMS.
pub prnu: Option<f64>,
/// From camera C1: dark signal nonuniformity, a fraction RMS.
pub dsnu: Option<f64>,
/// From camera C1: the fraction of hot pixels and their dark current's multiple.
pub hot_pixels: Option<[f64; 2]>,
/// From camera C1: the seed of the fixed pattern.
pub pattern_seed: Option<u64>,
/// The Gaussian blur's sigma in pixels, from camera C1: one for every channel, one
/// per channel, or 0 for none.
pub psf_sigma: Option<Sigma>,
@@ -174,6 +182,15 @@ impl SceneFile {
if let Some(shot) = c.shot_noise {
camera.detector.shot_noise = shot;
}
set(&mut camera.detector.prnu, c.prnu);
set(&mut camera.detector.dsnu, c.dsnu);
if let Some([fraction, multiple]) = c.hot_pixels {
camera.detector.hot_pixel_fraction = fraction;
camera.detector.hot_pixel_multiple = multiple;
}
if let Some(seed) = c.pattern_seed {
camera.detector.pattern_seed = seed;
}
if let Some([u, v]) = c.principal_point {
camera.principal_point = Some(Vector2::new(u, v));
}
@@ -453,7 +470,8 @@ mod tests {
fn camera_overrides_apply() {
let file: SceneFile = toml::from_str(&format!(
"{MINIMAL}\n[camera]\nwidth = 640\nexposure = 2e-3\nadc_bits = 10\nread_noise = 2.5\n\
dark_current = 0\nshot_noise = false\n"
dark_current = 0\nshot_noise = false\nprnu = 0.02\nhot_pixels = [1e-3, 20]\n\
pattern_seed = 9\n"
))
.unwrap();
let camera = file.camera();
@@ -465,6 +483,8 @@ mod tests {
(d.read_noise, d.dark_current, d.shot_noise),
(2.5, 0.0, false)
);
assert_eq!((d.prnu, d.dsnu, d.pattern_seed), (0.02, 0.1, 9));
assert_eq!((d.hot_pixel_fraction, d.hot_pixel_multiple), (1e-3, 20.0));
for (line, psf) in [
(