use blazediff_shared::Image;
use super::types::{BoundingBox, ChangeRegion, ChangeType};
pub fn detect_shifts(
regions: &mut [ChangeRegion],
img1: &Image,
img2: &Image,
mask: &[bool],
width: u32,
) {
let deletions: Vec<usize> = regions
.iter()
.enumerate()
.filter(|(_, r)| r.change_type == ChangeType::Deletion)
.map(|(i, _)| i)
.collect();
let appearances: Vec<usize> = regions
.iter()
.enumerate()
.filter(|(_, r)| {
matches!(
r.change_type,
ChangeType::Addition | ChangeType::ContentChange | ChangeType::ColorChange
)
})
.map(|(i, _)| i)
.collect();
let mut matched = std::collections::HashSet::new();
for &d in &deletions {
for &a in &appearances {
if matched.contains(&d) || matched.contains(&a) {
continue;
}
let w_ratio = regions[d].bbox.width as f64 / regions[a].bbox.width.max(1) as f64;
let h_ratio = regions[d].bbox.height as f64 / regions[a].bbox.height.max(1) as f64;
if !(0.55..=1.82).contains(&w_ratio) || !(0.55..=1.82).contains(&h_ratio) {
continue;
}
let px_ratio = regions[d].pixel_count as f64 / regions[a].pixel_count.max(1) as f64;
if !(0.65..=1.55).contains(&px_ratio) {
continue;
}
let (mean_d, std_d) = luminance_stats(img1, mask, ®ions[d].bbox, width);
let (mean_a, std_a) = luminance_stats(img2, mask, ®ions[a].bbox, width);
let mean_diff = (mean_d - mean_a).abs() / 255.0;
let std_diff = (std_d - std_a).abs() / 255.0;
if mean_diff < 0.20 && std_diff < 0.15 {
matched.insert(d);
matched.insert(a);
}
}
}
for &idx in &matched {
regions[idx].change_type = ChangeType::Shift;
}
}
pub fn luminance_stats(img: &Image, mask: &[bool], bbox: &BoundingBox, width: u32) -> (f64, f64) {
let mut sum = 0.0f64;
let mut sum_sq = 0.0f64;
let mut count = 0u64;
for y in bbox.y..bbox.y + bbox.height {
for x in bbox.x..bbox.x + bbox.width {
let idx = (y * width + x) as usize;
if mask[idx] {
let pos = idx * 4;
let lum = 0.299 * img.data[pos] as f64
+ 0.587 * img.data[pos + 1] as f64
+ 0.114 * img.data[pos + 2] as f64;
sum += lum;
sum_sq += lum * lum;
count += 1;
}
}
}
if count == 0 {
return (0.0, 0.0);
}
let mean = sum / count as f64;
let variance = (sum_sq / count as f64 - mean * mean).max(0.0);
(mean, variance.sqrt())
}