1 //! Functions for performing affine transformations.
2
3 use crate::ImageBuffer;
4 use crate::image::{GenericImage, GenericImageView};
5 use crate::error::{ImageError, ParameterError, ParameterErrorKind};
6 use crate::traits::Pixel;
7
8 /// Rotate an image 90 degrees clockwise.
rotate90<I: GenericImageView>( image: &I, ) -> ImageBuffer<I::Pixel, Vec<<I::Pixel as Pixel>::Subpixel>> where I::Pixel: 'static,9 pub fn rotate90<I: GenericImageView>(
10 image: &I,
11 ) -> ImageBuffer<I::Pixel, Vec<<I::Pixel as Pixel>::Subpixel>>
12 where I::Pixel: 'static,
13 {
14 let (width, height) = image.dimensions();
15 let mut out = ImageBuffer::new(height, width);
16 let _ = rotate90_in(image, &mut out);
17 out
18 }
19
20 /// Rotate an image 180 degrees clockwise.
rotate180<I: GenericImageView>( image: &I, ) -> ImageBuffer<I::Pixel, Vec<<I::Pixel as Pixel>::Subpixel>> where I::Pixel: 'static,21 pub fn rotate180<I: GenericImageView>(
22 image: &I,
23 ) -> ImageBuffer<I::Pixel, Vec<<I::Pixel as Pixel>::Subpixel>>
24 where I::Pixel: 'static,
25 {
26 let (width, height) = image.dimensions();
27 let mut out = ImageBuffer::new(width, height);
28 let _ = rotate180_in(image, &mut out);
29 out
30 }
31
32 /// Rotate an image 270 degrees clockwise.
rotate270<I: GenericImageView>( image: &I, ) -> ImageBuffer<I::Pixel, Vec<<I::Pixel as Pixel>::Subpixel>> where I::Pixel: 'static,33 pub fn rotate270<I: GenericImageView>(
34 image: &I,
35 ) -> ImageBuffer<I::Pixel, Vec<<I::Pixel as Pixel>::Subpixel>>
36 where I::Pixel: 'static,
37 {
38 let (width, height) = image.dimensions();
39 let mut out = ImageBuffer::new(height, width);
40 let _ = rotate270_in(image, &mut out);
41 out
42 }
43
44 /// Rotate an image 90 degrees clockwise and put the result into the destination [`ImageBuffer`].
rotate90_in<I, Container>( image: &I, destination: &mut ImageBuffer<I::Pixel, Container> ) -> crate::ImageResult<()> where I: GenericImageView, I::Pixel: 'static, Container: std::ops::DerefMut<Target = [<I::Pixel as Pixel>::Subpixel]>45 pub fn rotate90_in<I, Container>(
46 image: &I,
47 destination: &mut ImageBuffer<I::Pixel, Container>
48 ) -> crate::ImageResult<()> where
49 I: GenericImageView,
50 I::Pixel: 'static,
51 Container: std::ops::DerefMut<Target = [<I::Pixel as Pixel>::Subpixel]>
52 {
53 let ((w0, h0), (w1, h1)) = (image.dimensions(), destination.dimensions());
54 if w0 != h1 || h0 != w1 {
55 return Err(ImageError::Parameter(ParameterError::from_kind(
56 ParameterErrorKind::DimensionMismatch,
57 )));
58 }
59
60 for y in 0..h0 {
61 for x in 0..w0 {
62 let p = image.get_pixel(x, y);
63 destination.put_pixel(h0 - y - 1, x, p);
64 }
65 }
66 Ok(())
67 }
68
69 /// Rotate an image 180 degrees clockwise and put the result into the destination [`ImageBuffer`].
rotate180_in<I, Container>( image: &I, destination: &mut ImageBuffer<I::Pixel, Container> ) -> crate::ImageResult<()> where I: GenericImageView, I::Pixel: 'static, Container: std::ops::DerefMut<Target = [<I::Pixel as Pixel>::Subpixel]>70 pub fn rotate180_in<I, Container>(
71 image: &I,
72 destination: &mut ImageBuffer<I::Pixel, Container>
73 ) -> crate::ImageResult<()> where
74 I: GenericImageView,
75 I::Pixel: 'static,
76 Container: std::ops::DerefMut<Target = [<I::Pixel as Pixel>::Subpixel]>
77 {
78 let ((w0, h0), (w1, h1)) = (image.dimensions(), destination.dimensions());
79 if w0 != w1 || h0 != h1 {
80 return Err(ImageError::Parameter(ParameterError::from_kind(
81 ParameterErrorKind::DimensionMismatch,
82 )));
83 }
84
85 for y in 0..h0 {
86 for x in 0..w0 {
87 let p = image.get_pixel(x, y);
88 destination.put_pixel(w0 - x - 1, h0 - y - 1, p);
89 }
90 }
91 Ok(())
92 }
93
94 /// Rotate an image 270 degrees clockwise and put the result into the destination [`ImageBuffer`].
rotate270_in<I, Container>( image: &I, destination: &mut ImageBuffer<I::Pixel, Container> ) -> crate::ImageResult<()> where I: GenericImageView, I::Pixel: 'static, Container: std::ops::DerefMut<Target = [<I::Pixel as Pixel>::Subpixel]>95 pub fn rotate270_in<I, Container>(
96 image: &I,
97 destination: &mut ImageBuffer<I::Pixel, Container>
98 ) -> crate::ImageResult<()> where
99 I: GenericImageView,
100 I::Pixel: 'static,
101 Container: std::ops::DerefMut<Target = [<I::Pixel as Pixel>::Subpixel]>
102 {
103 let ((w0, h0), (w1, h1)) = (image.dimensions(), destination.dimensions());
104 if w0 != h1 || h0 != w1 {
105 return Err(ImageError::Parameter(ParameterError::from_kind(
106 ParameterErrorKind::DimensionMismatch,
107 )));
108 }
109
110 for y in 0..h0 {
111 for x in 0..w0 {
112 let p = image.get_pixel(x, y);
113 destination.put_pixel(y, w0 - x - 1, p);
114 }
115 }
116 Ok(())
117 }
118
119 /// Flip an image horizontally
flip_horizontal<I: GenericImageView>( image: &I, ) -> ImageBuffer<I::Pixel, Vec<<I::Pixel as Pixel>::Subpixel>> where I::Pixel: 'static,120 pub fn flip_horizontal<I: GenericImageView>(
121 image: &I,
122 ) -> ImageBuffer<I::Pixel, Vec<<I::Pixel as Pixel>::Subpixel>>
123 where I::Pixel: 'static,
124 {
125 let (width, height) = image.dimensions();
126 let mut out = ImageBuffer::new(width, height);
127 let _ = flip_horizontal_in(image, &mut out);
128 out
129 }
130
131 /// Flip an image vertically
flip_vertical<I: GenericImageView>( image: &I, ) -> ImageBuffer<I::Pixel, Vec<<I::Pixel as Pixel>::Subpixel>> where I::Pixel: 'static,132 pub fn flip_vertical<I: GenericImageView>(
133 image: &I,
134 ) -> ImageBuffer<I::Pixel, Vec<<I::Pixel as Pixel>::Subpixel>>
135 where I::Pixel: 'static,
136 {
137 let (width, height) = image.dimensions();
138 let mut out = ImageBuffer::new(width, height);
139 let _ = flip_vertical_in(image, &mut out);
140 out
141 }
142
143 /// Flip an image horizontally and put the result into the destination [`ImageBuffer`].
flip_horizontal_in<I, Container>( image: &I, destination: &mut ImageBuffer<I::Pixel, Container> ) -> crate::ImageResult<()> where I: GenericImageView, I::Pixel: 'static, Container: std::ops::DerefMut<Target = [<I::Pixel as Pixel>::Subpixel]>144 pub fn flip_horizontal_in<I, Container>(
145 image: &I,
146 destination: &mut ImageBuffer<I::Pixel, Container>
147 ) -> crate::ImageResult<()> where
148 I: GenericImageView,
149 I::Pixel: 'static,
150 Container: std::ops::DerefMut<Target = [<I::Pixel as Pixel>::Subpixel]>
151 {
152 let ((w0, h0), (w1, h1)) = (image.dimensions(), destination.dimensions());
153 if w0 != w1 || h0 != h1 {
154 return Err(ImageError::Parameter(ParameterError::from_kind(
155 ParameterErrorKind::DimensionMismatch,
156 )));
157 }
158
159 for y in 0..h0 {
160 for x in 0..w0 {
161 let p = image.get_pixel(x, y);
162 destination.put_pixel(w0 - x - 1, y, p);
163 }
164 }
165 Ok(())
166 }
167
168 /// Flip an image vertically and put the result into the destination [`ImageBuffer`].
flip_vertical_in<I, Container>( image: &I, destination: &mut ImageBuffer<I::Pixel, Container> ) -> crate::ImageResult<()> where I: GenericImageView, I::Pixel: 'static, Container: std::ops::DerefMut<Target = [<I::Pixel as Pixel>::Subpixel]>169 pub fn flip_vertical_in<I, Container>(
170 image: &I,
171 destination: &mut ImageBuffer<I::Pixel, Container>
172 ) -> crate::ImageResult<()> where
173 I: GenericImageView,
174 I::Pixel: 'static,
175 Container: std::ops::DerefMut<Target = [<I::Pixel as Pixel>::Subpixel]>
176 {
177 let ((w0, h0), (w1, h1)) = (image.dimensions(), destination.dimensions());
178 if w0 != w1 || h0 != h1 {
179 return Err(ImageError::Parameter(ParameterError::from_kind(
180 ParameterErrorKind::DimensionMismatch,
181 )));
182 }
183
184 for y in 0..h0 {
185 for x in 0..w0 {
186 let p = image.get_pixel(x, y);
187 destination.put_pixel(x, h0 - 1 - y, p);
188 }
189 }
190 Ok(())
191 }
192
193 /// Rotate an image 180 degrees clockwise in place.
rotate180_in_place<I: GenericImage>(image: &mut I)194 pub fn rotate180_in_place<I: GenericImage>(image: &mut I) {
195 let (width, height) = image.dimensions();
196
197 for y in 0..height / 2 {
198 for x in 0..width {
199 let p = image.get_pixel(x, y);
200
201 let x2 = width - x - 1;
202 let y2 = height - y - 1;
203
204 let p2 = image.get_pixel(x2, y2);
205 image.put_pixel(x, y, p2);
206 image.put_pixel(x2, y2, p);
207 }
208 }
209
210 if height % 2 != 0 {
211 let middle = height / 2;
212
213 for x in 0..width / 2 {
214 let p = image.get_pixel(x, middle);
215 let x2 = width - x - 1;
216
217 let p2 = image.get_pixel(x2, middle);
218 image.put_pixel(x, middle, p2);
219 image.put_pixel(x2, middle, p);
220 }
221 }
222 }
223
224 /// Flip an image horizontally in place.
flip_horizontal_in_place<I: GenericImage>(image: &mut I)225 pub fn flip_horizontal_in_place<I: GenericImage>(image: &mut I) {
226 let (width, height) = image.dimensions();
227
228 for y in 0..height {
229 for x in 0..width / 2 {
230 let x2 = width - x - 1;
231 let p2 = image.get_pixel(x2, y);
232 let p = image.get_pixel(x, y);
233 image.put_pixel(x2, y, p);
234 image.put_pixel(x, y, p2);
235 }
236 }
237 }
238
239 /// Flip an image vertically in place.
flip_vertical_in_place<I: GenericImage>(image: &mut I)240 pub fn flip_vertical_in_place<I: GenericImage>(image: &mut I) {
241 let (width, height) = image.dimensions();
242
243 for y in 0..height / 2 {
244 for x in 0..width {
245 let y2 = height - y - 1;
246 let p2 = image.get_pixel(x, y2);
247 let p = image.get_pixel(x, y);
248 image.put_pixel(x, y2, p);
249 image.put_pixel(x, y, p2);
250 }
251 }
252 }
253
254 #[cfg(test)]
255 mod test {
256 use super::{
257 flip_horizontal, flip_horizontal_in_place, flip_vertical, flip_vertical_in_place,
258 rotate180, rotate180_in_place, rotate270, rotate90,
259 };
260 use crate::{GrayImage, ImageBuffer};
261 use crate::image::GenericImage;
262 use crate::traits::Pixel;
263
264 macro_rules! assert_pixels_eq {
265 ($actual:expr, $expected:expr) => {{
266 let actual_dim = $actual.dimensions();
267 let expected_dim = $expected.dimensions();
268
269 if actual_dim != expected_dim {
270 panic!(
271 "dimensions do not match. \
272 actual: {:?}, expected: {:?}",
273 actual_dim, expected_dim
274 )
275 }
276
277 let diffs = pixel_diffs($actual, $expected);
278
279 if !diffs.is_empty() {
280 let mut err = "pixels do not match. ".to_string();
281
282 let diff_messages = diffs
283 .iter()
284 .take(5)
285 .map(|d| format!("\nactual: {:?}, expected {:?} ", d.0, d.1))
286 .collect::<Vec<_>>()
287 .join("");
288
289 err.push_str(&diff_messages);
290 panic!(err)
291 }
292 }};
293 }
294
295 #[test]
test_rotate90()296 fn test_rotate90() {
297 let image: GrayImage =
298 ImageBuffer::from_raw(3, 2, vec![00u8, 01u8, 02u8, 10u8, 11u8, 12u8]).unwrap();
299
300 let expected: GrayImage =
301 ImageBuffer::from_raw(2, 3, vec![10u8, 00u8, 11u8, 01u8, 12u8, 02u8]).unwrap();
302
303 assert_pixels_eq!(&rotate90(&image), &expected);
304 }
305
306 #[test]
test_rotate180()307 fn test_rotate180() {
308 let image: GrayImage =
309 ImageBuffer::from_raw(3, 2, vec![00u8, 01u8, 02u8, 10u8, 11u8, 12u8]).unwrap();
310
311 let expected: GrayImage =
312 ImageBuffer::from_raw(3, 2, vec![12u8, 11u8, 10u8, 02u8, 01u8, 00u8]).unwrap();
313
314 assert_pixels_eq!(&rotate180(&image), &expected);
315 }
316
317 #[test]
test_rotate270()318 fn test_rotate270() {
319 let image: GrayImage =
320 ImageBuffer::from_raw(3, 2, vec![00u8, 01u8, 02u8, 10u8, 11u8, 12u8]).unwrap();
321
322 let expected: GrayImage =
323 ImageBuffer::from_raw(2, 3, vec![02u8, 12u8, 01u8, 11u8, 00u8, 10u8]).unwrap();
324
325 assert_pixels_eq!(&rotate270(&image), &expected);
326 }
327
328 #[test]
test_rotate180_in_place()329 fn test_rotate180_in_place() {
330 let mut image: GrayImage =
331 ImageBuffer::from_raw(3, 2, vec![00u8, 01u8, 02u8, 10u8, 11u8, 12u8]).unwrap();
332
333 let expected: GrayImage =
334 ImageBuffer::from_raw(3, 2, vec![12u8, 11u8, 10u8, 02u8, 01u8, 00u8]).unwrap();
335
336 rotate180_in_place(&mut image);
337
338 assert_pixels_eq!(&image, &expected);
339 }
340
341 #[test]
test_flip_horizontal()342 fn test_flip_horizontal() {
343 let image: GrayImage =
344 ImageBuffer::from_raw(3, 2, vec![00u8, 01u8, 02u8, 10u8, 11u8, 12u8]).unwrap();
345
346 let expected: GrayImage =
347 ImageBuffer::from_raw(3, 2, vec![02u8, 01u8, 00u8, 12u8, 11u8, 10u8]).unwrap();
348
349 assert_pixels_eq!(&flip_horizontal(&image), &expected);
350 }
351
352 #[test]
test_flip_vertical()353 fn test_flip_vertical() {
354 let image: GrayImage =
355 ImageBuffer::from_raw(3, 2, vec![00u8, 01u8, 02u8, 10u8, 11u8, 12u8]).unwrap();
356
357 let expected: GrayImage =
358 ImageBuffer::from_raw(3, 2, vec![10u8, 11u8, 12u8, 00u8, 01u8, 02u8]).unwrap();
359
360 assert_pixels_eq!(&flip_vertical(&image), &expected);
361 }
362
363 #[test]
test_flip_horizontal_in_place()364 fn test_flip_horizontal_in_place() {
365 let mut image: GrayImage =
366 ImageBuffer::from_raw(3, 2, vec![00u8, 01u8, 02u8, 10u8, 11u8, 12u8]).unwrap();
367
368 let expected: GrayImage =
369 ImageBuffer::from_raw(3, 2, vec![02u8, 01u8, 00u8, 12u8, 11u8, 10u8]).unwrap();
370
371 flip_horizontal_in_place(&mut image);
372
373 assert_pixels_eq!(&image, &expected);
374 }
375
376 #[test]
test_flip_vertical_in_place()377 fn test_flip_vertical_in_place() {
378 let mut image: GrayImage =
379 ImageBuffer::from_raw(3, 2, vec![00u8, 01u8, 02u8, 10u8, 11u8, 12u8]).unwrap();
380
381 let expected: GrayImage =
382 ImageBuffer::from_raw(3, 2, vec![10u8, 11u8, 12u8, 00u8, 01u8, 02u8]).unwrap();
383
384 flip_vertical_in_place(&mut image);
385
386 assert_pixels_eq!(&image, &expected);
387 }
388
pixel_diffs<I, J, P>(left: &I, right: &J) -> Vec<((u32, u32, P), (u32, u32, P))> where I: GenericImage<Pixel = P>, J: GenericImage<Pixel = P>, P: Pixel + Eq,389 fn pixel_diffs<I, J, P>(left: &I, right: &J) -> Vec<((u32, u32, P), (u32, u32, P))>
390 where
391 I: GenericImage<Pixel = P>,
392 J: GenericImage<Pixel = P>,
393 P: Pixel + Eq,
394 {
395 left.pixels()
396 .zip(right.pixels())
397 .filter(|&(p, q)| p != q)
398 .collect::<Vec<_>>()
399 }
400 }
401