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
use summed_area::SummedArea;
use std::ops::{Add, Sub};
pub use imgref::*;
pub use rgb::RGB;
use rgb::ComponentMap;
pub fn maximum_symmetric_surround_saliency(image: Img<&[u8]>) -> Img<Vec<u16>> {
maximum_symmetric_surround_saliency_generic::<u8, u32, u16>(image)
}
pub fn maximum_symmetric_surround_saliency_rgb(image: Img<&[RGB<u8>]>) -> Img<Vec<u16>> {
maximum_symmetric_surround_saliency_generic::<RGB<u8>, RGB<u32>, u16>(image)
}
pub fn maximum_symmetric_surround_saliency_f32(image: Img<&[f32]>) -> Img<Vec<f32>> {
maximum_symmetric_surround_saliency_generic::<f32, f32, f32>(image)
}
fn maximum_symmetric_surround_saliency_generic<I, O, Res>(image: Img<&[I]>) -> Img<Vec<Res>>
where I: Copy + AreaDiff<O, Res>, O: From<I> + Default + Copy + Add<Output=O> + Sub<Output=O> {
let integral_img = SummedArea::new(image);
let (width, height) = (image.width() as u32, image.height() as u32);
let mut sal_map = Vec::with_capacity(width as usize * height as usize);
for y in 0..height {
let y_size = y.min(height - y);
let y1 = y.saturating_sub(y_size);
let y2 = (y + y_size).min(height - 1);
for x in 0..width {
let x_size = x.min(width - x);
let x1 = x.saturating_sub(x_size);
let x2 = (x + x_size).min(width - 1);
let area = (x2 - x1 + 1) * (y2 - y1 + 1);
let diff = image[(x, y)].area_diff(integral_img.sum_range(x1 as usize..x2 as usize, y1 as usize..y2 as usize), area);
sal_map.push(diff);
}
}
Img::new(sal_map, width as usize, height as usize)
}
trait AreaDiff<Sums, Res> {
fn area_diff(self, sum: Sums, area: u32) -> Res;
}
impl AreaDiff<u32, u16> for u8 {
fn area_diff(self, sum: u32, area: u32) -> u16 {
let diff = ((sum / area) as i16 - self as i16) as i32;
diff.pow(2) as u16
}
}
impl AreaDiff<f32, f32> for f32 {
fn area_diff(self, sum: f32, area: u32) -> f32 {
let diff = (sum / area as f32) - self;
diff * diff
}
}
impl AreaDiff<RGB<u32>, u16> for RGB<u8> {
fn area_diff(self, sum: RGB<u32>, area: u32) -> u16 {
let tmp = sum.map(|s| (s / area) as i16) - self.map(|c| c as i16);
let diff = tmp.r.max(tmp.g).max(tmp.b) as i32;
diff.pow(2) as u16
}
}
#[test]
fn oversized_input_is_ok() {
let img = ImgVec::new(vec![127u8; 35*18], 33, 17);
let res = maximum_symmetric_surround_saliency(img.as_ref());
assert_eq!(res.buf().len(), 33*17);
}