use crate::error::Result;
use crate::feature::{array_to_image, image_to_array};
use image::{DynamicImage, GrayImage};
use scirs2_core::ndarray::Array2;
#[allow(dead_code)]
pub fn laplacian_edges(
img: &DynamicImage,
threshold: f32,
use_diagonal: bool,
) -> Result<GrayImage> {
let array = image_to_array(img)?;
let (height, width) = array.dim();
let mut edges = Array2::zeros(array.dim());
for y in 1..(height - 1) {
for x in 1..(width - 1) {
let laplacian = if use_diagonal {
-array[[y - 1, x - 1]]
+ -array[[y - 1, x]]
+ -array[[y - 1, x + 1]]
+ -array[[y, x - 1]]
+ 8.0 * array[[y, x]]
+ -array[[y, x + 1]]
+ -array[[y + 1, x - 1]]
+ -array[[y + 1, x]]
+ -array[[y + 1, x + 1]]
} else {
0.0 * array[[y - 1, x - 1]]
+ -array[[y - 1, x]]
+ 0.0 * array[[y - 1, x + 1]]
+ -array[[y, x - 1]]
+ 4.0 * array[[y, x]]
+ -array[[y, x + 1]]
+ 0.0 * array[[y + 1, x - 1]]
+ -array[[y + 1, x]]
+ 0.0 * array[[y + 1, x + 1]]
};
if laplacian.abs() > threshold {
edges[[y, x]] = 1.0;
}
}
}
array_to_image(&edges)
}
#[allow(dead_code)]
pub fn laplacian_of_gaussian(img: &DynamicImage, sigma: f32, threshold: f32) -> Result<GrayImage> {
use crate::preprocessing::gaussian_blur;
let blurred = gaussian_blur(img, sigma)?;
laplacian_edges(&blurred, threshold, true)
}
#[allow(dead_code)]
pub fn laplacian_zero_crossing(_img: &DynamicImage, usediagonal: bool) -> Result<GrayImage> {
let array = image_to_array(_img)?;
let (height, width) = array.dim();
let mut laplacian = Array2::zeros(array.dim());
for y in 1..(height - 1) {
for x in 1..(width - 1) {
laplacian[[y, x]] = if usediagonal {
-array[[y - 1, x - 1]]
+ -array[[y - 1, x]]
+ -array[[y - 1, x + 1]]
+ -array[[y, x - 1]]
+ 8.0 * array[[y, x]]
+ -array[[y, x + 1]]
+ -array[[y + 1, x - 1]]
+ -array[[y + 1, x]]
+ -array[[y + 1, x + 1]]
} else {
-array[[y - 1, x]]
+ -array[[y, x - 1]]
+ 4.0 * array[[y, x]]
+ -array[[y, x + 1]]
+ -array[[y + 1, x]]
};
}
}
let mut edges = Array2::zeros(array.dim());
for y in 1..(height - 1) {
for x in 1..(width - 1) {
let center = laplacian[[y, x]];
let has_crossing = (center * laplacian[[y - 1, x]] < 0.0)
|| (center * laplacian[[y + 1, x]] < 0.0)
|| (center * laplacian[[y, x - 1]] < 0.0)
|| (center * laplacian[[y, x + 1]] < 0.0);
if usediagonal {
let has_diagonal_crossing = (center * laplacian[[y - 1, x - 1]] < 0.0)
|| (center * laplacian[[y - 1, x + 1]] < 0.0)
|| (center * laplacian[[y + 1, x - 1]] < 0.0)
|| (center * laplacian[[y + 1, x + 1]] < 0.0);
if has_crossing || has_diagonal_crossing {
edges[[y, x]] = 1.0;
}
} else if has_crossing {
edges[[y, x]] = 1.0;
}
}
}
array_to_image(&edges)
}
#[cfg(test)]
mod tests {
use super::*;
use image::Luma;
#[test]
fn test_laplacian_4_connected() {
let mut img = GrayImage::new(10, 10);
for y in 0..10 {
for x in 0..10 {
let value = if (3..=6).contains(&x) && (3..=6).contains(&y) {
255
} else {
0
};
img.put_pixel(x, y, Luma([value]));
}
}
let dynamic_img = DynamicImage::ImageLuma8(img);
let result = laplacian_edges(&dynamic_img, 0.1, false);
assert!(result.is_ok());
let edges = result.expect("Operation failed");
let mut has_edge = false;
for y in 2..8 {
if edges.get_pixel(2, y)[0] > 0 || edges.get_pixel(7, y)[0] > 0 {
has_edge = true;
break;
}
}
assert!(has_edge, "Should detect square edges");
}
#[test]
fn test_laplacian_8_connected() {
let img = GrayImage::new(10, 10);
let dynamic_img = DynamicImage::ImageLuma8(img);
let result_4 = laplacian_edges(&dynamic_img, 0.1, false);
let result_8 = laplacian_edges(&dynamic_img, 0.1, true);
assert!(result_4.is_ok());
assert!(result_8.is_ok());
}
#[test]
fn test_laplacian_of_gaussian() {
let img = GrayImage::new(10, 10);
let dynamic_img = DynamicImage::ImageLuma8(img);
let result = laplacian_of_gaussian(&dynamic_img, 1.0, 0.1);
assert!(result.is_ok());
}
#[test]
fn test_zero_crossing() {
let mut img = GrayImage::new(10, 10);
for y in 0..10 {
for x in 0..10 {
let value = ((x * 255) / 10) as u8;
img.put_pixel(x, y, Luma([value]));
}
}
let dynamic_img = DynamicImage::ImageLuma8(img);
let result = laplacian_zero_crossing(&dynamic_img, true);
assert!(result.is_ok());
}
}