1 //! Functions for performing affine transformations.
2
3 use buffer::{ImageBuffer, Pixel};
4 use image::GenericImageView;
5
6 /// Rotate an image 90 degrees clockwise.
7 // TODO: Is the 'static bound on `I` really required? Can we avoid it?
rotate90<I: GenericImageView + 'static>( image: &I, ) -> ImageBuffer<I::Pixel, Vec<<I::Pixel as Pixel>::Subpixel>> where I::Pixel: 'static, <I::Pixel as Pixel>::Subpixel: 'static,8 pub fn rotate90<I: GenericImageView + 'static>(
9 image: &I,
10 ) -> ImageBuffer<I::Pixel, Vec<<I::Pixel as Pixel>::Subpixel>>
11 where
12 I::Pixel: 'static,
13 <I::Pixel as Pixel>::Subpixel: 'static,
14 {
15 let (width, height) = image.dimensions();
16 let mut out = ImageBuffer::new(height, width);
17
18 for y in 0..height {
19 for x in 0..width {
20 let p = image.get_pixel(x, y);
21 out.put_pixel(height - 1 - y, x, p);
22 }
23 }
24
25 out
26 }
27
28 /// Rotate an image 180 degrees clockwise.
29 // TODO: Is the 'static bound on `I` really required? Can we avoid it?
rotate180<I: GenericImageView + 'static>( image: &I, ) -> ImageBuffer<I::Pixel, Vec<<I::Pixel as Pixel>::Subpixel>> where I::Pixel: 'static, <I::Pixel as Pixel>::Subpixel: 'static,30 pub fn rotate180<I: GenericImageView + 'static>(
31 image: &I,
32 ) -> ImageBuffer<I::Pixel, Vec<<I::Pixel as Pixel>::Subpixel>>
33 where
34 I::Pixel: 'static,
35 <I::Pixel as Pixel>::Subpixel: 'static,
36 {
37 let (width, height) = image.dimensions();
38 let mut out = ImageBuffer::new(width, height);
39
40 for y in 0..height {
41 for x in 0..width {
42 let p = image.get_pixel(x, y);
43 out.put_pixel(width - 1 - x, height - 1 - y, p);
44 }
45 }
46
47 out
48 }
49
50 /// Rotate an image 270 degrees clockwise.
51 // TODO: Is the 'static bound on `I` really required? Can we avoid it?
rotate270<I: GenericImageView + 'static>( image: &I, ) -> ImageBuffer<I::Pixel, Vec<<I::Pixel as Pixel>::Subpixel>> where I::Pixel: 'static, <I::Pixel as Pixel>::Subpixel: 'static,52 pub fn rotate270<I: GenericImageView + 'static>(
53 image: &I,
54 ) -> ImageBuffer<I::Pixel, Vec<<I::Pixel as Pixel>::Subpixel>>
55 where
56 I::Pixel: 'static,
57 <I::Pixel as Pixel>::Subpixel: 'static,
58 {
59 let (width, height) = image.dimensions();
60 let mut out = ImageBuffer::new(height, width);
61
62 for y in 0..height {
63 for x in 0..width {
64 let p = image.get_pixel(x, y);
65 out.put_pixel(y, width - 1 - x, p);
66 }
67 }
68
69 out
70 }
71
72 /// Flip an image horizontally
73 // TODO: Is the 'static bound on `I` really required? Can we avoid it?
flip_horizontal<I: GenericImageView + 'static>( image: &I, ) -> ImageBuffer<I::Pixel, Vec<<I::Pixel as Pixel>::Subpixel>> where I::Pixel: 'static, <I::Pixel as Pixel>::Subpixel: 'static,74 pub fn flip_horizontal<I: GenericImageView + 'static>(
75 image: &I,
76 ) -> ImageBuffer<I::Pixel, Vec<<I::Pixel as Pixel>::Subpixel>>
77 where
78 I::Pixel: 'static,
79 <I::Pixel as Pixel>::Subpixel: 'static,
80 {
81 let (width, height) = image.dimensions();
82 let mut out = ImageBuffer::new(width, height);
83
84 for y in 0..height {
85 for x in 0..width {
86 let p = image.get_pixel(x, y);
87 out.put_pixel(width - 1 - x, y, p);
88 }
89 }
90
91 out
92 }
93
94 /// Flip an image vertically
95 // TODO: Is the 'static bound on `I` really required? Can we avoid it?
flip_vertical<I: GenericImageView + 'static>( image: &I, ) -> ImageBuffer<I::Pixel, Vec<<I::Pixel as Pixel>::Subpixel>> where I::Pixel: 'static, <I::Pixel as Pixel>::Subpixel: 'static,96 pub fn flip_vertical<I: GenericImageView + 'static>(
97 image: &I,
98 ) -> ImageBuffer<I::Pixel, Vec<<I::Pixel as Pixel>::Subpixel>>
99 where
100 I::Pixel: 'static,
101 <I::Pixel as Pixel>::Subpixel: 'static,
102 {
103 let (width, height) = image.dimensions();
104 let mut out = ImageBuffer::new(width, height);
105
106 for y in 0..height {
107 for x in 0..width {
108 let p = image.get_pixel(x, y);
109 out.put_pixel(x, height - 1 - y, p);
110 }
111 }
112
113 out
114 }
115
116 #[cfg(test)]
117 mod test {
118 use super::{flip_horizontal, flip_vertical, rotate180, rotate270, rotate90};
119 use buffer::{GrayImage, ImageBuffer, Pixel};
120 use image::GenericImage;
121
122 macro_rules! assert_pixels_eq {
123 ($actual:expr, $expected:expr) => {{
124 let actual_dim = $actual.dimensions();
125 let expected_dim = $expected.dimensions();
126
127 if actual_dim != expected_dim {
128 panic!(
129 "dimensions do not match. \
130 actual: {:?}, expected: {:?}",
131 actual_dim, expected_dim
132 )
133 }
134
135 let diffs = pixel_diffs($actual, $expected);
136
137 if !diffs.is_empty() {
138 let mut err = "pixels do not match. ".to_string();
139
140 let diff_messages = diffs
141 .iter()
142 .take(5)
143 .map(|d| format!("\nactual: {:?}, expected {:?} ", d.0, d.1))
144 .collect::<Vec<_>>()
145 .join("");
146
147 err.push_str(&diff_messages);
148 panic!(err)
149 }
150 }};
151 }
152
153 #[test]
test_rotate90()154 fn test_rotate90() {
155 let image: GrayImage =
156 ImageBuffer::from_raw(3, 2, vec![00u8, 01u8, 02u8, 10u8, 11u8, 12u8]).unwrap();
157
158 let expected: GrayImage =
159 ImageBuffer::from_raw(2, 3, vec![10u8, 00u8, 11u8, 01u8, 12u8, 02u8]).unwrap();
160
161 assert_pixels_eq!(&rotate90(&image), &expected);
162 }
163
164 #[test]
test_rotate180()165 fn test_rotate180() {
166 let image: GrayImage =
167 ImageBuffer::from_raw(3, 2, vec![00u8, 01u8, 02u8, 10u8, 11u8, 12u8]).unwrap();
168
169 let expected: GrayImage =
170 ImageBuffer::from_raw(3, 2, vec![12u8, 11u8, 10u8, 02u8, 01u8, 00u8]).unwrap();
171
172 assert_pixels_eq!(&rotate180(&image), &expected);
173 }
174
175 #[test]
test_rotate270()176 fn test_rotate270() {
177 let image: GrayImage =
178 ImageBuffer::from_raw(3, 2, vec![00u8, 01u8, 02u8, 10u8, 11u8, 12u8]).unwrap();
179
180 let expected: GrayImage =
181 ImageBuffer::from_raw(2, 3, vec![02u8, 12u8, 01u8, 11u8, 00u8, 10u8]).unwrap();
182
183 assert_pixels_eq!(&rotate270(&image), &expected);
184 }
185
186 #[test]
test_flip_horizontal()187 fn test_flip_horizontal() {
188 let image: GrayImage =
189 ImageBuffer::from_raw(3, 2, vec![00u8, 01u8, 02u8, 10u8, 11u8, 12u8]).unwrap();
190
191 let expected: GrayImage =
192 ImageBuffer::from_raw(3, 2, vec![02u8, 01u8, 00u8, 12u8, 11u8, 10u8]).unwrap();
193
194 assert_pixels_eq!(&flip_horizontal(&image), &expected);
195 }
196
197 #[test]
test_flip_vertical()198 fn test_flip_vertical() {
199 let image: GrayImage =
200 ImageBuffer::from_raw(3, 2, vec![00u8, 01u8, 02u8, 10u8, 11u8, 12u8]).unwrap();
201
202 let expected: GrayImage =
203 ImageBuffer::from_raw(3, 2, vec![10u8, 11u8, 12u8, 00u8, 01u8, 02u8]).unwrap();
204
205 assert_pixels_eq!(&flip_vertical(&image), &expected);
206 }
207
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,208 fn pixel_diffs<I, J, P>(left: &I, right: &J) -> Vec<((u32, u32, P), (u32, u32, P))>
209 where
210 I: GenericImage<Pixel = P>,
211 J: GenericImage<Pixel = P>,
212 P: Pixel + Eq,
213 {
214 left.pixels()
215 .zip(right.pixels())
216 .filter(|&(p, q)| p != q)
217 .collect::<Vec<_>>()
218 }
219 }
220