Put the -1 on the greens along the chroma axis, not across it
The Malvar "R at green in R row" kernel weights the two greens two sites away along the row at -1 and the pair up and down the column at +1/2. The shader had the two swapped, in the comment as well as the code, so the transcription checked against itself. Both sum to zero and reconstruct a flat patch exactly, which is all the tests fed it. On an edge the correction at green sites is half strength and the false colour doubles: 0.375 against 0.19 on a grey step, and a blue/yellow zipper around every clipped highlight at 1:1. The other three kernels and the CFA tables were right. A grey vertical step now runs through the pass; the transposed kernel fails it at 0.375.
This commit is contained in:
@@ -1347,6 +1347,53 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_grey_step_edge_stays_grey() {
|
||||
// A flat patch cannot tell the Malvar kernels from any other set of
|
||||
// weights that sum to zero. An edge can. A grey vertical step, so
|
||||
// every photosite records the same profile, must come back with the
|
||||
// three channels close together on both sides; any spread is false
|
||||
// colour from interpolating across the edge.
|
||||
//
|
||||
// The bound is set by the paper's kernels, which peak at 0.19 here.
|
||||
// With the ±2 terms of the green-site kernels transposed — the bug
|
||||
// this test was written against — the peak is 0.375.
|
||||
let Some(ctx) = ctx() else { return };
|
||||
let d = Demosaicer::new(&ctx).expect("demosaicer");
|
||||
|
||||
let size = 32u32;
|
||||
let white = 16383u16;
|
||||
let mut raw = flat_cfa(CfaPattern::Rggb, size, [0, 0, 0], 0, white);
|
||||
for y in 0..size {
|
||||
for x in size / 2..size {
|
||||
raw.data[(y * size + x) as usize] = white;
|
||||
}
|
||||
}
|
||||
|
||||
let img = d.run(&raw).expect("demosaic");
|
||||
let px = read_rgba(&ctx, &img);
|
||||
let (w, _) = img.size();
|
||||
|
||||
let mut worst = (0.0f32, 0u32, 0u32);
|
||||
for y in 2..size - 2 {
|
||||
for x in 2..size - 2 {
|
||||
let p = px[(y * w + x) as usize];
|
||||
let spread = (p[0] - p[1]).abs().max((p[2] - p[1]).abs());
|
||||
if spread > worst.0 {
|
||||
worst = (spread, x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
assert!(
|
||||
worst.0 < 0.25,
|
||||
"false colour of {} at ({}, {}) on a grey edge — the green-site \
|
||||
kernels are interpolating across the edge",
|
||||
worst.0,
|
||||
worst.1,
|
||||
worst.2
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn output_is_free_of_nan_and_negatives() {
|
||||
// f16 NaN propagates silently through every later stage; a negative
|
||||
|
||||
Reference in New Issue
Block a user