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]
|
#[test]
|
||||||
fn output_is_free_of_nan_and_negatives() {
|
fn output_is_free_of_nan_and_negatives() {
|
||||||
// f16 NaN propagates silently through every later stage; a negative
|
// f16 NaN propagates silently through every later stage; a negative
|
||||||
|
|||||||
@@ -160,12 +160,18 @@ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
|
|||||||
// Green is measured. Red and blue are interpolated from their own
|
// Green is measured. Red and blue are interpolated from their own
|
||||||
// axis, with a correction from the green Laplacian.
|
// axis, with a correction from the green Laplacian.
|
||||||
//
|
//
|
||||||
// Malvar "G at R/B locations" kernels, transposed per axis:
|
// Malvar "R at green in R row" kernel, and its transpose:
|
||||||
// chroma along the row: (5c + 4(w1+e1) - (nw+ne+sw+se) - (n2+s2) + 0.5(w2+e2)) / 8
|
// chroma along the row: (5c + 4(w1+e1) - (nw+ne+sw+se) - (w2+e2) + 0.5(n2+s2)) / 8
|
||||||
|
//
|
||||||
|
// The -1 goes on the two greens *along* the chroma axis and the +0.5
|
||||||
|
// on the pair across it. Transposed, both kernels still sum to zero
|
||||||
|
// and reconstruct a flat patch exactly, but on an edge the correction
|
||||||
|
// at green sites is half strength and the false colour doubles: a
|
||||||
|
// blue/yellow zipper around every clipped highlight.
|
||||||
let along_row =
|
let along_row =
|
||||||
(5.0 * c + 4.0 * (w1 + e1) - diag1 - vert2 + 0.5 * horiz2) * 0.125;
|
(5.0 * c + 4.0 * (w1 + e1) - diag1 - horiz2 + 0.5 * vert2) * 0.125;
|
||||||
let along_col =
|
let along_col =
|
||||||
(5.0 * c + 4.0 * (n1 + s1) - diag1 - horiz2 + 0.5 * vert2) * 0.125;
|
(5.0 * c + 4.0 * (n1 + s1) - diag1 - vert2 + 0.5 * horiz2) * 0.125;
|
||||||
|
|
||||||
let red_horizontal = red_is_horizontal(gid.x, gid.y);
|
let red_horizontal = red_is_horizontal(gid.x, gid.y);
|
||||||
let r = select(along_col, along_row, red_horizontal);
|
let r = select(along_col, along_row, red_horizontal);
|
||||||
|
|||||||
Reference in New Issue
Block a user