This repository has been archived by the owner on Jul 19, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
derivatives.rs
127 lines (118 loc) · 3.45 KB
/
derivatives.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
use crate::image::{fill_border, GrayFloatImage};
use ndarray::{s, Array2, ArrayView2, ArrayViewMut2};
/// Compute the Scharr derivative horizontally
///
/// The implementation of this function is using a separable kernel, for speed.
///
/// # Arguments
/// * `image` - the input image.
/// * `sigma_size` - the scale of the derivative.
///
/// # Return value
/// Output image derivative (an image.)
pub fn scharr_horizontal(image: &GrayFloatImage, sigma_size: u32) -> GrayFloatImage {
let img_horizontal = scharr_axis(
&image,
sigma_size,
FilterDirection::Horizontal,
FilterOrder::Main,
);
scharr_axis(
&img_horizontal,
sigma_size,
FilterDirection::Vertical,
FilterOrder::Off,
)
}
/// Compute the Scharr derivative vertically
///
/// The implementation of this function is using a separable kernel, for speed.
///
/// # Arguments
/// * `image` - the input image.
/// * `sigma_size` - the scale of the derivative.
///
/// # Return value
/// Output image derivative (an image.)
pub fn scharr_vertical(image: &GrayFloatImage, sigma_size: u32) -> GrayFloatImage {
let img_horizontal = scharr_axis(
&image,
sigma_size,
FilterDirection::Horizontal,
FilterOrder::Off,
);
scharr_axis(
&img_horizontal,
sigma_size,
FilterDirection::Vertical,
FilterOrder::Main,
)
}
/// Multiplies and accumulates
fn accumulate_mul_offset(
mut accumulator: ArrayViewMut2<f32>,
source: ArrayView2<f32>,
val: f32,
border: usize,
xoff: usize,
yoff: usize,
) {
assert_eq!(source.dim(), accumulator.dim());
let dims = source.dim();
let mut accumulator =
accumulator.slice_mut(s![border..dims.0 - border, border..dims.1 - border]);
accumulator.scaled_add(
val,
&source.slice(s![
yoff..dims.0 + yoff - 2 * border,
xoff..dims.1 + xoff - 2 * border
]),
);
}
#[derive(Copy, Clone, Debug, PartialEq)]
enum FilterDirection {
Horizontal,
Vertical,
}
#[derive(Copy, Clone, Debug, PartialEq)]
enum FilterOrder {
Main,
Off,
}
fn scharr_axis(
image: &GrayFloatImage,
sigma_size: u32,
dir: FilterDirection,
order: FilterOrder,
) -> GrayFloatImage {
let mut output = Array2::<f32>::zeros([image.height(), image.width()]);
// Get the border size (we wont fill in this border width of the output).
let border = sigma_size as usize;
// Difference between middle and sides of main axis filter.
let w = 10.0 / 3.0;
// Side intensity of filter.
let norm = (1.0 / (2.0 * f64::from(sigma_size) * (w + 2.0))) as f32;
// Middle intensity of filter.
let middle = norm * w as f32;
let mut offsets = match order {
FilterOrder::Main => vec![
(norm, [border, 0]),
(middle, [border, border]),
(norm, [border, 2 * border]),
],
FilterOrder::Off => vec![(-1.0, [border, 0]), (1.0, [border, 2 * border])],
};
if dir == FilterDirection::Horizontal {
// Swap the offsets if the filter is a horizontal filter.
for (_, [x, y]) in &mut offsets {
std::mem::swap(x, y);
}
}
// Accumulate the three components.
for (val, [x, y]) in offsets {
accumulate_mul_offset(output.view_mut(), image.ref_array2(), val, border, x, y);
}
let mut output = GrayFloatImage::from_array2(output);
fill_border(&mut output, border);
output
}