pub mod antialias;
pub mod classify;
pub mod cli;
pub mod cluster;
pub mod color;
pub mod compare;
pub mod denoise;
pub mod dilate;
pub mod merge;
pub mod output;
pub mod region;
pub fn diff_images(
baseline: &image::RgbaImage,
candidate: &image::RgbaImage,
options: DiffOptions,
) -> region::DiffResult {
let mut compare_result = compare::compare(
baseline,
candidate,
&compare::CompareOptions {
threshold: options.threshold,
detect_antialias: options.detect_antialias,
},
);
denoise::denoise(
&mut compare_result.diff_mask,
compare_result.width,
compare_result.height,
options.denoise,
);
let dilated_mask = dilate::dilate(
&compare_result.diff_mask,
compare_result.width,
compare_result.height,
options.dilate,
);
let labels = cluster::label_components(
&dilated_mask,
compare_result.width,
compare_result.height,
options.connectivity,
);
let mut regions = region::extract_regions(
&labels,
&compare_result.delta_map,
compare_result.width,
compare_result.height,
options.min_region_size,
);
merge::merge_regions(&mut regions, options.merge_distance);
classify::classify_regions(
&mut regions,
baseline,
candidate,
&compare_result.diff_mask,
compare_result.width,
compare_result.height,
);
let total_pixels = compare_result.width as u64 * compare_result.height as u64;
let changed_pixels = compare_result.diff_mask.iter().filter(|&&v| v).count() as u64;
let dimension_mismatch =
if baseline.width() != candidate.width() || baseline.height() != candidate.height() {
Some(region::DimensionMismatch {
baseline: region::Dimensions {
width: baseline.width(),
height: baseline.height(),
},
candidate: region::Dimensions {
width: candidate.width(),
height: candidate.height(),
},
})
} else {
None
};
region::DiffResult {
dimensions: region::Dimensions {
width: compare_result.width,
height: compare_result.height,
},
stats: region::DiffStats {
changed_pixels,
total_pixels,
diff_percentage: if total_pixels > 0 {
(changed_pixels as f64 / total_pixels as f64) * 100.0
} else {
0.0
},
region_count: regions.len(),
antialiased_pixels: compare_result.aa_pixel_count,
},
is_match: regions.is_empty(),
regions,
dimension_mismatch,
}
}
pub struct DiffOptions {
pub threshold: f64,
pub detect_antialias: bool,
pub connectivity: u8,
pub min_region_size: u32,
pub denoise: u32,
pub dilate: u32,
pub merge_distance: u32,
}
impl Default for DiffOptions {
fn default() -> Self {
Self {
threshold: 0.1,
detect_antialias: true,
connectivity: 8,
min_region_size: 25,
denoise: 25,
dilate: 0,
merge_distance: 50,
}
}
}