visioncortex/shape/
image_operations.rs1pub use bit_vec::BitVec;
2
3use crate::{BinaryImage, Shape};
4
5impl BinaryImage {
6 pub fn operation(
7 &self,
8 other: &BinaryImage,
9 operator: impl FnMut((&mut u8, &u8)),
10 ) -> BinaryImage {
11 assert_eq!(self.width, other.width);
12 assert_eq!(self.height, other.height);
13 let mut i = self.pixels.to_bytes();
14 let u = other.pixels.to_bytes();
15 i.iter_mut().zip(u.iter()).for_each(operator);
16 BinaryImage {
17 pixels: BitVec::from_bytes(&i),
18 width: self.width,
19 height: self.height,
20 }
21 }
22
23 pub fn negative(&self) -> BinaryImage {
24 let i = self.pixels.to_bytes();
25 use std::ops::Not;
26 let ii = i.iter().map(|x| x.not()).collect::<Vec<u8>>();
27 BinaryImage {
28 pixels: BitVec::from_bytes(&ii.as_slice()),
29 width: self.width,
30 height: self.height,
31 }
32 }
33
34 pub fn diff(&self, other: &BinaryImage) -> BinaryImage {
35 self.operation(other, |(x1, x2)| *x1 ^= *x2)
36 }
37
38 pub fn union(&self, other: &BinaryImage) -> BinaryImage {
39 self.operation(other, |(x1, x2)| *x1 |= *x2)
40 }
41
42 pub fn intersect(&self, other: &BinaryImage) -> BinaryImage {
43 self.operation(other, |(x1, x2)| *x1 &= *x2)
44 }
45
46 pub fn clustered_diff(&self, other: &BinaryImage) -> u32 {
47 self.diff(other).significance(self.area(), std::u32::MAX)
48 }
49
50 pub fn significance(&self, area: u64, threshold: u32) -> u32 {
52 let clusters = self.to_clusters(false);
53 let mut diff: u64 = 0;
54 let scale = 4 * 128 * 128;
55 let divisor = area * self.width as u64;
56 let threshold_u64 = threshold as u64 * divisor;
57 for cluster in clusters.iter() {
58 let size = cluster.size() as u64;
59 let cluster_image = cluster.to_binary_image();
60 let boundary = Shape::image_boundary_list(&cluster_image);
61 let skeleton = cluster_image.to_skeleton();
62 diff += scale * size *
63 skeleton.stat.mean as u64 *
64 skeleton.stat.count as u64 /
65 boundary.len() as u64;
66 if diff >= threshold_u64 {
67 break;
68 }
69 }
70 (diff / divisor) as u32
71 }
72
73 pub fn diff_and_count(&self, other: &BinaryImage) -> usize {
74 assert_eq!(self.width, other.width);
75 assert_eq!(self.height, other.height);
76 let mut i = self.pixels.to_bytes();
77 let u = other.pixels.to_bytes();
78 i.iter_mut().zip(u.iter()).for_each(|(x1, x2)| *x1 ^= *x2);
79 while i.len() % 4 != 0 {
80 i.push(0);
81 }
82 let mut count = 0;
83 for ii in (0..i.len()).step_by(4) {
84 count += Self::popcount(u32::from_be_bytes([i[ii], i[ii + 1], i[ii + 2], i[ii + 3]]))
85 as usize;
86 }
87 count
88 }
89
90 #[inline(always)]
91 pub fn popcount(i: u32) -> u32 {
92 i.count_ones()
93 }
94
95 pub fn stroke(&self, s: u32) -> BinaryImage {
97 let mut new_image = BinaryImage::new_w_h(self.width + s as usize, self.height + s as usize);
98 let ox = (s as usize) >> 1;
99 let oy = (s as usize) >> 1;
100 let ss = (s >> 1) as i32;
101 for y in 0..self.height {
102 for x in 0..self.width {
103 let v = self.get_pixel(x, y);
104 if v {
105 for yy in -ss..ss {
106 for xx in -ss..ss {
107 if (((xx * xx + yy * yy) as f64).sqrt() as i32) < ss {
108 new_image.set_pixel(
109 (x as i32 + xx + ox as i32) as usize,
110 (y as i32 + yy + oy as i32) as usize,
111 true,
112 );
113 }
114 }
115 }
116 }
117 }
118 }
119 new_image
120 }
121}
122
123#[cfg(test)]
124mod tests {
125 use super::*;
126
127 #[test]
128 fn image_diff() {
129 let mut a = BinaryImage::new_w_h(2, 2);
130 a.set_pixel(0, 0, true);
131 let mut b = BinaryImage::new_w_h(2, 2);
132 b.set_pixel(1, 1, true);
133 assert_eq!(a.diff_and_count(&b), 2);
134
135 let mut a = BinaryImage::new_w_h(3, 3);
136 a.set_pixel(1, 1, true);
137 let mut b = BinaryImage::new_w_h(3, 3);
138 b.set_pixel(1, 1, true);
139 assert_eq!(a.diff_and_count(&b), 0);
140
141 let mut a = BinaryImage::new_w_h(3, 3);
142 a.set_pixel(0, 0, true);
143 a.set_pixel(1, 1, true);
144 let mut b = BinaryImage::new_w_h(3, 3);
145 b.set_pixel(1, 1, true);
146 b.set_pixel(2, 2, true);
147 assert_eq!(a.diff_and_count(&b), 2);
148 }
149
150 #[test]
151 fn negative_image() {
152 assert_eq!(
153 BinaryImage::from_string(&(
154 "*-*\n".to_owned() +
155 "-*-\n" +
156 "*-*\n"
157 ))
158 .negative()
159 .to_string(),
160 BinaryImage::from_string(&(
161 "-*-\n".to_owned() +
162 "*-*\n" +
163 "-*-\n"
164 )).to_string()
165 );
166 }
167}