visioncortex/
sampler.rs

1use crate::{BinaryImage, BoundingRect};
2
3/// For sampling and resizing binary images
4pub struct Sampler {
5    pub image: BinaryImage,
6}
7
8impl Sampler {
9    pub fn new(image: &BinaryImage) -> Sampler {
10        let size = std::cmp::max(image.width, image.height);
11        Self::new_with_size(image, size)
12    }
13
14    pub fn new_with_size(image: &BinaryImage, sampler_size: usize) -> Sampler {
15        Self::new_with_size_crop(image, sampler_size, Default::default())
16    }
17
18    pub fn new_with_size_crop(
19        image: &BinaryImage,
20        sampler_size: usize,
21        crop: BoundingRect,
22    ) -> Sampler {
23        let new_image;
24        assert_eq!(crop.width(), crop.height());
25        if crop.is_empty() && image.width == image.height && image.width == sampler_size {
26            new_image = image.clone();
27        } else if !crop.is_empty()
28            && crop.width() as usize == sampler_size
29            && crop.height() as usize == sampler_size
30        {
31            new_image = image.crop_with_rect(crop);
32        } else {
33            new_image = Self::resample_square_image(&image, crop, sampler_size);
34        }
35        Sampler { image: new_image }
36    }
37
38    /// Resize an image of any size into a square image while keeping the aspect ratio of content.
39    /// Would empty fill expanded area.
40    pub fn resample_square_image(
41        image: &BinaryImage,
42        crop: BoundingRect,
43        new_size: usize,
44    ) -> BinaryImage {
45        let mut new_image = BinaryImage::new_w_h(new_size, new_size);
46        let new_size = new_size as i32;
47        let crop = if !crop.is_empty() {
48            crop
49        } else {
50            BoundingRect::new_x_y_w_h(0, 0, image.width as i32, image.height as i32)
51        };
52        let image_size = std::cmp::max(crop.width(), crop.height()) as i32;
53        let ox = (image_size - crop.width() as i32) >> 1;
54        let oy = (image_size - crop.height() as i32) >> 1;
55        for y in 0..new_size {
56            for x in 0..new_size {
57                let xx = x * image_size / new_size - ox + crop.left;
58                let yy = y * image_size / new_size - oy + crop.top;
59                new_image.set_pixel(x as usize, y as usize, image.get_pixel_safe(xx, yy));
60            }
61        }
62        new_image
63    }
64
65    pub fn resample_image(image: &BinaryImage, new_width: usize, new_height: usize) -> BinaryImage {
66        Self::resample_image_with_crop(image, Default::default(), new_width, new_height)
67    }
68
69    pub fn resample_image_with_crop(
70        image: &BinaryImage,
71        crop: BoundingRect,
72        new_width: usize,
73        new_height: usize,
74    ) -> BinaryImage {
75        let mut new_image = BinaryImage::new_w_h(new_width, new_height);
76        Self::resample_image_with_crop_to_image(
77            image,
78            crop,
79            &mut new_image,
80            BoundingRect::new_x_y_w_h(0, 0, new_width as i32, new_height as i32),
81        );
82        new_image
83    }
84
85    pub fn resample_image_with_crop_to_image(
86        src: &BinaryImage,
87        src_rect: BoundingRect,
88        dst: &mut BinaryImage,
89        dst_rect: BoundingRect,
90    ) {
91        Self::resample_image_with_crop_to_image_overlay(src, src_rect, dst, dst_rect, false);
92    }
93
94    pub fn resample_image_with_crop_to_image_overlay(
95        src: &BinaryImage,
96        src_rect: BoundingRect,
97        dst: &mut BinaryImage,
98        dst_rect: BoundingRect,
99        overlay: bool,
100    ) {
101        let src_rect = if !src_rect.is_empty() {
102            src_rect
103        } else {
104            BoundingRect::new_x_y_w_h(0, 0, src.width as i32, src.height as i32)
105        };
106        for y in 0..dst_rect.height() {
107            for x in 0..dst_rect.width() {
108                let s = 1;
109                let xx =
110                    s * x as i32 * src_rect.width() / dst_rect.width() as i32
111                    + s * src_rect.left;
112                let yy =
113                    s * y as i32 * src_rect.height() / dst_rect.height() as i32
114                    + s * src_rect.top;
115                let pixel = src.get_pixel_safe(xx / s, yy / s);
116                if !overlay || pixel {
117                    // overlay: set pixel only if pixel is true
118                    // otherwise: set pixel anyway
119                    dst.set_pixel(
120                        (dst_rect.left + x) as usize,
121                        (dst_rect.top + y) as usize,
122                        pixel,
123                    );
124                }
125            }
126        }
127    }
128}
129
130impl Sampler {
131    pub fn size(&self) -> usize {
132        self.image.width
133    }
134
135    pub fn bounding_rect(&self) -> BoundingRect {
136        self.image.bounding_rect()
137    }
138
139    pub fn sample(&self, left: usize, top: usize, right: usize, bottom: usize) -> usize {
140        let mut count = 0;
141        for y in top..bottom {
142            for x in left..right {
143                if self.image.get_pixel(x, y) {
144                    count += 1;
145                }
146            }
147        }
148        count
149    }
150}
151
152#[allow(dead_code)]
153fn is_pow_of_four(n: usize) -> bool {
154    (1 << (2 * pow_of_four(n))) == n
155}
156
157fn pow_of_four(mut n: usize) -> usize {
158    let mut pow_of_4 = 0;
159    while n > 3 {
160        n >>= 2;
161        pow_of_4 += 1;
162    }
163    pow_of_4
164}
165
166#[cfg(test)]
167mod tests {
168    use super::*;
169
170    #[test]
171    fn sampler_2() {
172        let size = 2;
173        let mut image = BinaryImage::new_w_h(size, size);
174        image.set_pixel(0, 0, true);
175        image.set_pixel(1, 1, true);
176        let sampler = Sampler::new_with_size(&image, size);
177        assert_eq!(sampler.sample(0, 0, 1, 1), 1);
178        assert_eq!(sampler.sample(0, 1, 1, 2), 0);
179        assert_eq!(sampler.sample(1, 0, 2, 1), 0);
180        assert_eq!(sampler.sample(1, 1, 2, 2), 1);
181    }
182
183    #[test]
184    /// cropping a 4x4 image to a 2x2 image same as above
185    fn sampler_crop() {
186        let size = 4;
187        let mut image = BinaryImage::new_w_h(size,size);
188        image.set_pixel(1,1,true);
189        image.set_pixel(2,2,true);
190        let sampler = Sampler::new_with_size_crop(&image, 2, BoundingRect {
191            left: 1, top: 1, right: 3, bottom: 3
192        });
193        assert_eq!(sampler.image.width, 2);
194        assert_eq!(sampler.size(), 2);
195        assert_eq!(sampler.sample(0, 0, 1, 1), 1);
196        assert_eq!(sampler.sample(0, 1, 1, 2), 0);
197        assert_eq!(sampler.sample(1, 0, 2, 1), 0);
198        assert_eq!(sampler.sample(1, 1, 2, 2), 1);
199    }
200
201    #[test]
202    fn sampler_4() {
203        let size = 4;
204        let mut image = BinaryImage::new_w_h(size, size);
205        image.set_pixel(0, 0, true);
206        image.set_pixel(1, 1, true);
207        let sampler = Sampler::new_with_size(&image, size);
208        assert_eq!(sampler.sample(0, 0, size, size), 2); // whole
209        assert_eq!(sampler.sample(0, 0, size / 2, size / 2), 2);
210        assert_eq!(sampler.sample(size / 2, size / 2, size, size), 0);
211        assert_eq!(sampler.sample(0,0,1,1), 1);
212        assert_eq!(sampler.sample(1,1,size/2,size/2), 1);
213        let rect = sampler.bounding_rect();
214        assert_eq!(rect, BoundingRect { left: 0, right: 2, top: 0, bottom: 2 });
215        assert_eq!(rect.width(), 2);
216        assert_eq!(rect.height(), 2);
217    }
218
219    #[test]
220    fn sampler_upsize() {
221        let mut image = BinaryImage::new_w_h(4, 4);
222        image.set_pixel(1, 1, true);
223        println!("image:\n{}", image.to_string());
224        let sampler = Sampler::new_with_size(&image, 8);
225        assert_eq!(sampler.image.width, 8);
226        println!("upsized:\n{}", sampler.image.to_string());
227        assert_eq!(sampler.sample(2, 2, 4, 4), 4);
228        assert_eq!(sampler.sample(0, 0, 8, 8), 4);
229        assert_eq!(sampler.sample(4, 4, 8, 8), 0);
230    }
231
232    #[test]
233    fn sampler_crop_upsize() {
234        let mut image = BinaryImage::new_w_h(8,8);
235        image.set_pixel(2,2,true);
236        println!("image:\n{}", image.to_string());
237        let sampler = Sampler::new_with_size_crop(&image, 8, BoundingRect {
238            left: 1, top: 1, right: 5, bottom: 5
239        });
240        assert_eq!(sampler.image.width, 8);
241        println!("cropped & upsized:\n{}", sampler.image.to_string());
242        assert_eq!(sampler.sample(2, 2, 4, 4), 4);
243        assert_eq!(sampler.sample(0, 0, 8, 8), 4);
244        assert_eq!(sampler.sample(4, 4, 8, 8), 0);
245    }
246
247    #[test]
248    fn sampler_upsize_non_exact() {
249        let mut image = BinaryImage::new_w_h(6, 6);
250        image.set_pixel(1, 1, true);
251        let sampler = Sampler::new_with_size(&image, 8);
252        assert_eq!(sampler.sample(0, 0, 8, 8), 1);
253    }
254
255    #[test]
256    fn resample_image_2x2_to_4x2() {
257        let image = BinaryImage::from_string(&(
258            "*-\n".to_owned()+
259            "-*\n"));
260        assert_eq!(
261            Sampler::resample_image(&image, 4, 2).to_string(),
262            BinaryImage::from_string(&(
263                "**--\n".to_owned()+
264                "--**\n")).to_string()
265        );
266    }
267
268    #[test]
269    fn resample_image_2x2_to_4x2_crop() {
270        let image = BinaryImage::from_string(&(
271            "--\n".to_owned()+
272            "*-\n"+
273            "-*\n"));
274        let mut new_image = BinaryImage::new_w_h(4, 4);
275        Sampler::resample_image_with_crop_to_image(
276            &image, BoundingRect::new_x_y_w_h(0, 1, 2, 2),
277            &mut new_image, BoundingRect::new_x_y_w_h(0, 1, 4, 2),
278        );
279        assert_eq!(
280            new_image.to_string(),
281            BinaryImage::from_string(&(
282                "----\n".to_owned()+
283                "**--\n"+
284                "--**\n"+
285                "----\n")).to_string()
286        );
287    }
288
289    #[test]
290    fn resample_image_2x2_to_3x2() {
291        let image = BinaryImage::from_string(&(
292            "*-\n".to_owned()+
293            "-*\n"));
294        assert_eq!(
295            Sampler::resample_image(&image, 3, 2).to_string(),
296            BinaryImage::from_string(&(
297                "**-\n".to_owned()+
298                "--*\n")).to_string()
299        )
300    }
301
302    #[test]
303    fn resample_image_3x3_to_2x2() {
304        let mut image = BinaryImage::new_w_h(3, 3);
305        image.set_pixel(1, 1, true);
306        image.set_pixel(2, 1, true);
307        image.set_pixel(1, 2, true);
308        image.set_pixel(2, 2, true);
309        let new_image = Sampler::resample_image(&image, 2, 2);
310        assert_eq!(new_image.width, 2);
311        assert_eq!(new_image.height, 2);
312        assert_eq!(new_image.get_pixel(0, 0), false);
313        assert_eq!(new_image.get_pixel(0, 1), false);
314        assert_eq!(new_image.get_pixel(1, 0), false);
315        assert_eq!(new_image.get_pixel(1, 1), true);
316    }
317}