1use crate::image::{fill_border, GrayFloatImage};
2use ndarray::{s, Array2, ArrayView2, ArrayViewMut2};
3
4pub fn scharr_horizontal(image: &GrayFloatImage, sigma_size: u32) -> GrayFloatImage {
15 let img_horizontal = scharr_axis(
16 &image,
17 sigma_size,
18 FilterDirection::Horizontal,
19 FilterOrder::Main,
20 );
21 scharr_axis(
22 &img_horizontal,
23 sigma_size,
24 FilterDirection::Vertical,
25 FilterOrder::Off,
26 )
27}
28
29pub fn scharr_vertical(image: &GrayFloatImage, sigma_size: u32) -> GrayFloatImage {
40 let img_horizontal = scharr_axis(
41 &image,
42 sigma_size,
43 FilterDirection::Horizontal,
44 FilterOrder::Off,
45 );
46 scharr_axis(
47 &img_horizontal,
48 sigma_size,
49 FilterDirection::Vertical,
50 FilterOrder::Main,
51 )
52}
53
54fn accumulate_mul_offset(
56 mut accumulator: ArrayViewMut2<f32>,
57 source: ArrayView2<f32>,
58 val: f32,
59 border: usize,
60 xoff: usize,
61 yoff: usize,
62) {
63 assert_eq!(source.dim(), accumulator.dim());
64 let dims = source.dim();
65 let mut accumulator =
66 accumulator.slice_mut(s![border..dims.0 - border, border..dims.1 - border]);
67 accumulator.scaled_add(
68 val,
69 &source.slice(s![
70 yoff..dims.0 + yoff - 2 * border,
71 xoff..dims.1 + xoff - 2 * border
72 ]),
73 );
74}
75
76#[derive(Copy, Clone, Debug, PartialEq)]
77enum FilterDirection {
78 Horizontal,
79 Vertical,
80}
81
82#[derive(Copy, Clone, Debug, PartialEq)]
83enum FilterOrder {
84 Main,
85 Off,
86}
87
88fn scharr_axis(
89 image: &GrayFloatImage,
90 sigma_size: u32,
91 dir: FilterDirection,
92 order: FilterOrder,
93) -> GrayFloatImage {
94 let mut output = Array2::<f32>::zeros([image.height(), image.width()]);
95 let border = sigma_size as usize;
97 let w = 10.0 / 3.0;
99 let norm = (1.0 / (2.0 * f64::from(sigma_size) * (w + 2.0))) as f32;
101 let middle = norm * w as f32;
103
104 let mut offsets = match order {
105 FilterOrder::Main => vec![
106 (norm, [border, 0]),
107 (middle, [border, border]),
108 (norm, [border, 2 * border]),
109 ],
110 FilterOrder::Off => vec![(-1.0, [border, 0]), (1.0, [border, 2 * border])],
111 };
112
113 if dir == FilterDirection::Horizontal {
114 for (_, [x, y]) in &mut offsets {
116 std::mem::swap(x, y);
117 }
118 }
119
120 for (val, [x, y]) in offsets {
122 accumulate_mul_offset(output.view_mut(), image.ref_array2(), val, border, x, y);
123 }
124 let mut output = GrayFloatImage::from_array2(output);
125 fill_border(&mut output, border);
126 output
127}