use crate::dct::fmla;
use crate::image::Image3F;
const K_GABORISH: [f32; 5] = [
-0.092_359_15,
-0.039_253_623,
0.016_176_495,
0.000_834_584,
0.004_512_465,
];
pub(crate) fn gaborish_inverse(image: &mut Image3F, mul: f32) {
debug_assert!(mul >= 0.0);
let w_r = mul * K_GABORISH[0];
let w_d = mul * K_GABORISH[1];
let w_r2 = mul * K_GABORISH[2];
let w_l = mul * K_GABORISH[3];
let w_d2 = mul * K_GABORISH[4];
let mut sum = 1.0_f64;
sum += 4.0 * w_r as f64;
sum += 4.0 * w_r2 as f64;
sum += 4.0 * w_d as f64;
sum += 4.0 * w_d2 as f64;
sum += 8.0 * w_l as f64;
let norm = (1.0 / sum) as f32;
let n_c = norm;
let n_r = norm * w_r;
let n_d = norm * w_d;
let n_r2 = norm * w_r2;
let n_l = norm * w_l;
let n_d2 = norm * w_d2;
for c in 0..3 {
symmetric5_plane(image, c, n_c, n_r, n_d, n_r2, n_l, n_d2);
}
}
#[inline]
fn reflect(i: isize, len: isize) -> usize {
let mut j = i;
while j < 0 || j >= len {
if j < 0 {
j = -j - 1;
} else if j >= len {
j = 2 * len - j - 1;
}
}
j as usize
}
fn symmetric5_plane(
image: &mut Image3F,
c: usize,
n_c: f32,
n_r: f32,
n_d: f32,
n_r2: f32,
n_l: f32,
n_d2: f32,
) {
let xsize = image.xsize();
let ysize = image.ysize();
let mut out: Vec<f32> = vec![0.0; xsize * ysize];
let plane = image.plane(c);
for y in 0..ysize {
let iy = y as isize;
let y_m1 = reflect(iy - 1, ysize as isize);
let y_p1 = reflect(iy + 1, ysize as isize);
let y_m2 = reflect(iy - 2, ysize as isize);
let y_p2 = reflect(iy + 2, ysize as isize);
let row_0 = plane.row(y);
let row_m1 = plane.row(y_m1);
let row_p1 = plane.row(y_p1);
let row_m2 = plane.row(y_m2);
let row_p2 = plane.row(y_p2);
let dst_row = &mut out[y * xsize..(y + 1) * xsize];
assert!(dst_row.len() >= xsize);
let border = 2usize;
let center_start = border.min(xsize);
let center_end = xsize.saturating_sub(border);
for x in 0..center_start {
let ix = x as isize;
let x_m1 = reflect(ix - 1, xsize as isize);
let x_p1 = reflect(ix + 1, xsize as isize);
let x_m2 = reflect(ix - 2, xsize as isize);
let x_p2 = reflect(ix + 2, xsize as isize);
let s_r = row_m1[x] + row_p1[x] + row_0[x_m1] + row_0[x_p1];
let s_d = row_m1[x_m1] + row_m1[x_p1] + row_p1[x_m1] + row_p1[x_p1];
let s_r2 = row_m2[x] + row_p2[x] + row_0[x_m2] + row_0[x_p2];
let s_d2 = row_m2[x_m2] + row_m2[x_p2] + row_p2[x_m2] + row_p2[x_p2];
let s_l = row_m2[x_m1]
+ row_m2[x_p1]
+ row_m1[x_m2]
+ row_m1[x_p2]
+ row_p1[x_m2]
+ row_p1[x_p2]
+ row_p2[x_m1]
+ row_p2[x_p1];
let mut acc = n_c * row_0[x];
acc = fmla(n_r, s_r, acc);
acc = fmla(n_d, s_d, acc);
acc = fmla(n_r2, s_r2, acc);
acc = fmla(n_d2, s_d2, acc);
acc = fmla(n_l, s_l, acc);
dst_row[x] = acc;
}
if center_start < center_end {
assert!(center_start >= 2);
assert!(row_0.len() >= center_end + 2);
assert!(row_m1.len() >= center_end + 2);
assert!(row_p1.len() >= center_end + 2);
assert!(row_m2.len() >= center_end + 2);
assert!(row_p2.len() >= center_end + 2);
assert!(dst_row.len() >= center_end);
for x in center_start..center_end {
let ix = x as isize;
let x_m1 = (ix - 1) as usize;
let x_p1 = (ix + 1) as usize;
let x_m2 = (ix - 2) as usize;
let x_p2 = (ix + 2) as usize;
let s_r = row_m1[x] + row_p1[x] + row_0[x_m1] + row_0[x_p1];
let s_d = row_m1[x_m1] + row_m1[x_p1] + row_p1[x_m1] + row_p1[x_p1];
let s_r2 = row_m2[x] + row_p2[x] + row_0[x_m2] + row_0[x_p2];
let s_d2 = row_m2[x_m2] + row_m2[x_p2] + row_p2[x_m2] + row_p2[x_p2];
let s_l = row_m2[x_m1]
+ row_m2[x_p1]
+ row_m1[x_m2]
+ row_m1[x_p2]
+ row_p1[x_m2]
+ row_p1[x_p2]
+ row_p2[x_m1]
+ row_p2[x_p1];
let mut acc = n_c * row_0[x];
acc = fmla(n_r, s_r, acc);
acc = fmla(n_d, s_d, acc);
acc = fmla(n_r2, s_r2, acc);
acc = fmla(n_d2, s_d2, acc);
acc = fmla(n_l, s_l, acc);
dst_row[x] = acc;
}
}
for x in center_end..xsize {
let ix = x as isize;
let x_m1 = reflect(ix - 1, xsize as isize);
let x_p1 = reflect(ix + 1, xsize as isize);
let x_m2 = reflect(ix - 2, xsize as isize);
let x_p2 = reflect(ix + 2, xsize as isize);
let s_r = row_m1[x] + row_p1[x] + row_0[x_m1] + row_0[x_p1];
let s_d = row_m1[x_m1] + row_m1[x_p1] + row_p1[x_m1] + row_p1[x_p1];
let s_r2 = row_m2[x] + row_p2[x] + row_0[x_m2] + row_0[x_p2];
let s_d2 = row_m2[x_m2] + row_m2[x_p2] + row_p2[x_m2] + row_p2[x_p2];
let s_l = row_m2[x_m1]
+ row_m2[x_p1]
+ row_m1[x_m2]
+ row_m1[x_p2]
+ row_p1[x_m2]
+ row_p1[x_p2]
+ row_p2[x_m1]
+ row_p2[x_p1];
let mut acc = n_c * row_0[x];
acc = fmla(n_r, s_r, acc);
acc = fmla(n_d, s_d, acc);
acc = fmla(n_r2, s_r2, acc);
acc = fmla(n_d2, s_d2, acc);
acc = fmla(n_l, s_l, acc);
dst_row[x] = acc;
}
}
for y in 0..ysize {
let dst_row = image.plane_row_mut(c, y);
dst_row.copy_from_slice(&out[y * xsize..(y + 1) * xsize]);
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::image::Image3F;
#[test]
fn constant_input_unchanged() {
let mut img = Image3F::new(16, 16);
for c in 0..3 {
for y in 0..16 {
let row = img.plane_row_mut(c, y);
for x in 0..16 {
row[x] = 0.5;
}
}
}
gaborish_inverse(&mut img, 0.9908511);
for c in 0..3 {
for y in 0..16 {
let row = img.plane_row(c, y);
for x in 0..16 {
assert!(
(row[x] - 0.5).abs() < 1e-5,
"c={} y={} x={}: got {} want 0.5",
c,
y,
x,
row[x]
);
}
}
}
}
#[test]
fn sharpens_step_edge() {
let mut img = Image3F::new(16, 16);
for c in 0..3 {
for y in 0..16 {
let row = img.plane_row_mut(c, y);
for x in 0..16 {
row[x] = if x < 8 { 0.0 } else { 1.0 };
}
}
}
let orig_at_8 = 1.0_f32;
let orig_at_7 = 0.0_f32;
gaborish_inverse(&mut img, 0.9908511);
let new_at_8 = img.plane_row(0, 8)[8];
let new_at_7 = img.plane_row(0, 8)[7];
assert!(
new_at_8 > orig_at_8 - 0.001,
"value at edge+ should not collapse: {}",
new_at_8
);
assert!(
new_at_7 < orig_at_7 + 0.001,
"value at edge- should not bleed: {}",
new_at_7
);
}
}