imageproc/map.rs
1//! Functions for mapping over pixels, colors or subpixels of images.
2
3use image::{GenericImage, ImageBuffer, Luma, LumaA, Pixel, Primitive, Rgb, Rgba};
4
5use crate::definitions::Image;
6
7/// The type obtained by replacing the channel type of a given `Pixel` type.
8/// The output type must have the same name of channels as the input type, or
9/// several algorithms will produce incorrect results or panic.
10pub trait WithChannel<C: Primitive>: Pixel {
11 /// The new pixel type.
12 type Pixel: Pixel<Subpixel = C>;
13}
14
15/// Alias to make uses of `WithChannel` less syntactically noisy.
16pub type ChannelMap<Pix, Sub> = <Pix as WithChannel<Sub>>::Pixel;
17
18impl<T, U> WithChannel<U> for Rgb<T>
19where
20 Rgb<T>: Pixel<Subpixel = T>,
21 Rgb<U>: Pixel<Subpixel = U>,
22 T: Primitive,
23 U: Primitive,
24{
25 type Pixel = Rgb<U>;
26}
27
28impl<T, U> WithChannel<U> for Rgba<T>
29where
30 Rgba<T>: Pixel<Subpixel = T>,
31 Rgba<U>: Pixel<Subpixel = U>,
32 T: Primitive,
33 U: Primitive,
34{
35 type Pixel = Rgba<U>;
36}
37
38impl<T, U> WithChannel<U> for Luma<T>
39where
40 T: Primitive,
41 U: Primitive,
42{
43 type Pixel = Luma<U>;
44}
45
46impl<T, U> WithChannel<U> for LumaA<T>
47where
48 T: Primitive,
49 U: Primitive,
50{
51 type Pixel = LumaA<U>;
52}
53
54/// Applies `f` to each subpixel of the input image.
55///
56/// # Examples
57/// ```
58/// # extern crate image;
59/// # #[macro_use]
60/// # extern crate imageproc;
61/// # fn main() {
62/// use imageproc::map::map_subpixels;
63///
64/// let image = gray_image!(
65/// 1, 2;
66/// 3, 4);
67///
68/// let scaled = gray_image!(type: i16,
69/// -2, -4;
70/// -6, -8);
71///
72/// assert_pixels_eq!(
73/// map_subpixels(&image, |x| -2 * (x as i16)),
74/// scaled);
75/// # }
76/// ```
77pub fn map_subpixels<I, P, F, S>(image: &I, f: F) -> Image<ChannelMap<P, S>>
78where
79 I: GenericImage<Pixel = P>,
80 P: WithChannel<S>,
81 S: Primitive,
82 F: Fn(P::Subpixel) -> S,
83{
84 let (width, height) = image.dimensions();
85 let mut out: ImageBuffer<ChannelMap<P, S>, Vec<S>> = ImageBuffer::new(width, height);
86
87 for y in 0..height {
88 for x in 0..width {
89 let out_channels = out.get_pixel_mut(x, y).channels_mut();
90 for c in 0..P::CHANNEL_COUNT {
91 out_channels[c as usize] = f(unsafe {
92 *image
93 .unsafe_get_pixel(x, y)
94 .channels()
95 .get_unchecked(c as usize)
96 });
97 }
98 }
99 }
100
101 out
102}
103
104/// Applies `f` to each subpixel of the input image in place.
105///
106/// # Examples
107/// ```
108/// # extern crate image;
109/// # #[macro_use]
110/// # extern crate imageproc;
111/// # fn main() {
112/// use imageproc::map::map_subpixels_mut;
113///
114/// let mut image = gray_image!(
115/// 1, 2;
116/// 3, 4);
117///
118/// let want = gray_image!(
119/// 2, 4;
120/// 6, 8);
121///
122/// map_subpixels_mut(&mut image, |x| 2 * x);
123///
124/// assert_pixels_eq!(
125/// image,
126/// want);
127/// # }
128/// ```
129pub fn map_subpixels_mut<I, P, F>(image: &mut I, f: F)
130where
131 I: GenericImage<Pixel = P>,
132 P: Pixel,
133 F: Fn(P::Subpixel) -> P::Subpixel,
134{
135 let (width, height) = image.dimensions();
136
137 for y in 0..height {
138 for x in 0..width {
139 let mut pixel = image.get_pixel(x, y);
140 let channels = pixel.channels_mut();
141 for c in 0..P::CHANNEL_COUNT as usize {
142 channels[c] =
143 f(unsafe { *image.unsafe_get_pixel(x, y).channels().get_unchecked(c) });
144 }
145 image.put_pixel(x, y, pixel);
146 }
147 }
148}
149
150/// Applies `f` to the color of each pixel in the input image.
151///
152/// # Examples
153/// ```
154/// # extern crate image;
155/// # #[macro_use]
156/// # extern crate imageproc;
157/// # fn main() {
158/// use image::Rgb;
159/// use imageproc::map::map_colors;
160///
161/// let image = gray_image!(
162/// 1, 2;
163/// 3, 4);
164///
165/// let rgb = rgb_image!(
166/// [1, 2, 3], [2, 4, 6];
167/// [3, 6, 9], [4, 8, 12]);
168///
169/// assert_pixels_eq!(
170/// map_colors(&image, |p| { Rgb([p[0], (2 * p[0]), (3 * p[0])]) }),
171/// rgb);
172/// # }
173/// ```
174pub fn map_colors<I, P, Q, F>(image: &I, f: F) -> Image<Q>
175where
176 I: GenericImage<Pixel = P>,
177 P: Pixel,
178 Q: Pixel,
179 F: Fn(P) -> Q,
180{
181 let (width, height) = image.dimensions();
182 let mut out: ImageBuffer<Q, Vec<Q::Subpixel>> = ImageBuffer::new(width, height);
183
184 for y in 0..height {
185 for x in 0..width {
186 unsafe {
187 let pix = image.unsafe_get_pixel(x, y);
188 out.unsafe_put_pixel(x, y, f(pix));
189 }
190 }
191 }
192
193 out
194}
195
196/// Applies `f` to the color of each pixel in the input image in place.
197///
198/// # Examples
199/// ```
200/// # extern crate image;
201/// # #[macro_use]
202/// # extern crate imageproc;
203/// # fn main() {
204/// use image::Luma;
205/// use imageproc::map::map_colors_mut;
206///
207/// let mut image = gray_image!(
208/// 1, 2;
209/// 3, 4);
210///
211/// let want = gray_image!(
212/// 2, 4;
213/// 6, 8);
214///
215/// map_colors_mut(&mut image, |p| Luma([2 * p[0]]));
216///
217/// assert_pixels_eq!(
218/// image,
219/// want);
220/// # }
221/// ```
222pub fn map_colors_mut<I, P, F>(image: &mut I, f: F)
223where
224 I: GenericImage<Pixel = P>,
225 P: Pixel,
226 F: Fn(P) -> P,
227{
228 let (width, height) = image.dimensions();
229
230 for y in 0..height {
231 for x in 0..width {
232 unsafe {
233 let pix = image.unsafe_get_pixel(x, y);
234 image.unsafe_put_pixel(x, y, f(pix));
235 }
236 }
237 }
238}
239
240/// Applies `f` to the colors of the pixels in the input images.
241///
242/// Requires `image1` and `image2` to have the same dimensions.
243/// # Examples
244/// ```
245/// # extern crate image;
246/// # #[macro_use]
247/// # extern crate imageproc;
248/// # fn main() {
249/// use image::Luma;
250/// use imageproc::map::map_colors2;
251///
252/// let image1 = gray_image!(
253/// 1, 2,
254/// 3, 4
255/// );
256///
257/// let image2 = gray_image!(
258/// 10, 20,
259/// 30, 40
260/// );
261///
262/// let sum = gray_image!(
263/// 11, 22,
264/// 33, 44
265/// );
266///
267/// assert_pixels_eq!(
268/// map_colors2(&image1, &image2, |p, q| Luma([p[0] + q[0]])),
269/// sum
270/// );
271/// # }
272/// ```
273pub fn map_colors2<I, J, P, Q, R, F>(image1: &I, image2: &J, f: F) -> Image<R>
274where
275 I: GenericImage<Pixel = P>,
276 J: GenericImage<Pixel = Q>,
277 P: Pixel,
278 Q: Pixel,
279 R: Pixel,
280 F: Fn(P, Q) -> R,
281{
282 assert_eq!(image1.dimensions(), image2.dimensions());
283
284 let (width, height) = image1.dimensions();
285 let mut out: ImageBuffer<R, Vec<R::Subpixel>> = ImageBuffer::new(width, height);
286
287 for y in 0..height {
288 for x in 0..width {
289 unsafe {
290 let p = image1.unsafe_get_pixel(x, y);
291 let q = image2.unsafe_get_pixel(x, y);
292 out.unsafe_put_pixel(x, y, f(p, q));
293 }
294 }
295 }
296
297 out
298}
299
300/// Applies `f` to each pixel in the input image.
301///
302/// # Examples
303/// ```
304/// # extern crate image;
305/// # #[macro_use]
306/// # extern crate imageproc;
307/// # fn main() {
308/// use image::Rgb;
309/// use imageproc::map::map_pixels;
310///
311/// let image = gray_image!(
312/// 1, 2;
313/// 3, 4);
314///
315/// let rgb = rgb_image!(
316/// [1, 0, 0], [2, 1, 0];
317/// [3, 0, 1], [4, 1, 1]);
318///
319/// assert_pixels_eq!(
320/// map_pixels(&image, |x, y, p| {
321/// Rgb([p[0], x as u8, y as u8])
322/// }),
323/// rgb);
324/// # }
325/// ```
326pub fn map_pixels<I, P, Q, F>(image: &I, f: F) -> Image<Q>
327where
328 I: GenericImage<Pixel = P>,
329 P: Pixel,
330 Q: Pixel,
331 F: Fn(u32, u32, P) -> Q,
332{
333 let (width, height) = image.dimensions();
334 let mut out: ImageBuffer<Q, Vec<Q::Subpixel>> = ImageBuffer::new(width, height);
335
336 for y in 0..height {
337 for x in 0..width {
338 unsafe {
339 let pix = image.unsafe_get_pixel(x, y);
340 out.unsafe_put_pixel(x, y, f(x, y, pix));
341 }
342 }
343 }
344
345 out
346}
347
348/// Applies `f` to each pixel in the input image in place.
349///
350/// # Examples
351/// ```
352/// # extern crate image;
353/// # #[macro_use]
354/// # extern crate imageproc;
355/// # fn main() {
356/// use image::Luma;
357/// use imageproc::map::map_pixels_mut;
358///
359/// let mut image = gray_image!(
360/// 1, 2;
361/// 3, 4);
362///
363/// let want = gray_image!(
364/// 1, 3;
365/// 4, 6);
366///
367/// map_pixels_mut(&mut image, |x, y, p| {
368/// Luma([p[0] + x as u8 + y as u8])
369/// });
370///
371/// assert_pixels_eq!(
372/// image,
373/// want);
374/// # }
375/// ```
376pub fn map_pixels_mut<I, P, F>(image: &mut I, f: F)
377where
378 I: GenericImage<Pixel = P>,
379 P: Pixel,
380 F: Fn(u32, u32, P) -> P,
381{
382 let (width, height) = image.dimensions();
383
384 for y in 0..height {
385 for x in 0..width {
386 unsafe {
387 let pix = image.unsafe_get_pixel(x, y);
388 image.unsafe_put_pixel(x, y, f(x, y, pix));
389 }
390 }
391 }
392}
393
394/// Creates a grayscale image by extracting the red channel of an RGB image.
395///
396/// # Examples
397/// ```
398/// # extern crate image;
399/// # #[macro_use]
400/// # extern crate imageproc;
401/// # fn main() {
402/// use image::Luma;
403/// use imageproc::map::red_channel;
404///
405/// let image = rgb_image!(
406/// [1, 2, 3], [2, 4, 6];
407/// [3, 6, 9], [4, 8, 12]);
408///
409/// let expected = gray_image!(
410/// 1, 2;
411/// 3, 4);
412///
413/// let actual = red_channel(&image);
414/// assert_pixels_eq!(actual, expected);
415/// # }
416/// ```
417pub fn red_channel<I, C>(image: &I) -> Image<Luma<C>>
418where
419 I: GenericImage<Pixel = Rgb<C>>,
420 Rgb<C>: Pixel<Subpixel = C>,
421 C: Primitive,
422{
423 map_colors(image, |p| Luma([p[0]]))
424}
425
426/// Creates an RGB image by embedding a grayscale image in its red channel.
427///
428/// # Examples
429/// ```
430/// # extern crate image;
431/// # #[macro_use]
432/// # extern crate imageproc;
433/// # fn main() {
434/// use image::Luma;
435/// use imageproc::map::as_red_channel;
436///
437/// let image = gray_image!(
438/// 1, 2;
439/// 3, 4);
440///
441/// let expected = rgb_image!(
442/// [1, 0, 0], [2, 0, 0];
443/// [3, 0, 0], [4, 0, 0]);
444///
445/// let actual = as_red_channel(&image);
446/// assert_pixels_eq!(actual, expected);
447/// # }
448/// ```
449pub fn as_red_channel<I, C>(image: &I) -> Image<Rgb<C>>
450where
451 I: GenericImage<Pixel = Luma<C>>,
452 Rgb<C>: Pixel<Subpixel = C>,
453 C: Primitive,
454{
455 map_colors(image, |p| {
456 let mut cs = [C::zero(); 3];
457 cs[0] = p[0];
458 Rgb(cs)
459 })
460}
461
462/// Creates a grayscale image by extracting the green channel of an RGB image.
463///
464/// # Examples
465/// ```
466/// # extern crate image;
467/// # #[macro_use]
468/// # extern crate imageproc;
469/// # fn main() {
470/// use image::Luma;
471/// use imageproc::map::green_channel;
472///
473/// let image = rgb_image!(
474/// [1, 2, 3], [2, 4, 6];
475/// [3, 6, 9], [4, 8, 12]);
476///
477/// let expected = gray_image!(
478/// 2, 4;
479/// 6, 8);
480///
481/// let actual = green_channel(&image);
482/// assert_pixels_eq!(actual, expected);
483/// # }
484/// ```
485pub fn green_channel<I, C>(image: &I) -> Image<Luma<C>>
486where
487 I: GenericImage<Pixel = Rgb<C>>,
488 Rgb<C>: Pixel<Subpixel = C>,
489 C: Primitive,
490{
491 map_colors(image, |p| Luma([p[1]]))
492}
493
494/// Creates an RGB image by embedding a grayscale image in its green channel.
495///
496/// # Examples
497/// ```
498/// # extern crate image;
499/// # #[macro_use]
500/// # extern crate imageproc;
501/// # fn main() {
502/// use image::Luma;
503/// use imageproc::map::as_green_channel;
504///
505/// let image = gray_image!(
506/// 1, 2;
507/// 3, 4);
508///
509/// let expected = rgb_image!(
510/// [0, 1, 0], [0, 2, 0];
511/// [0, 3, 0], [0, 4, 0]);
512///
513/// let actual = as_green_channel(&image);
514/// assert_pixels_eq!(actual, expected);
515/// # }
516/// ```
517pub fn as_green_channel<I, C>(image: &I) -> Image<Rgb<C>>
518where
519 I: GenericImage<Pixel = Luma<C>>,
520 Rgb<C>: Pixel<Subpixel = C>,
521 C: Primitive,
522{
523 map_colors(image, |p| {
524 let mut cs = [C::zero(); 3];
525 cs[1] = p[0];
526 Rgb(cs)
527 })
528}
529
530/// Creates a grayscale image by extracting the blue channel of an RGB image.
531///
532/// # Examples
533/// ```
534/// # extern crate image;
535/// # #[macro_use]
536/// # extern crate imageproc;
537/// # fn main() {
538/// use image::Luma;
539/// use imageproc::map::blue_channel;
540///
541/// let image = rgb_image!(
542/// [1, 2, 3], [2, 4, 6];
543/// [3, 6, 9], [4, 8, 12]);
544///
545/// let expected = gray_image!(
546/// 3, 6;
547/// 9, 12);
548///
549/// let actual = blue_channel(&image);
550/// assert_pixels_eq!(actual, expected);
551/// # }
552/// ```
553pub fn blue_channel<I, C>(image: &I) -> Image<Luma<C>>
554where
555 I: GenericImage<Pixel = Rgb<C>>,
556 Rgb<C>: Pixel<Subpixel = C>,
557 C: Primitive,
558{
559 map_colors(image, |p| Luma([p[2]]))
560}
561
562/// Creates an RGB image by embedding a grayscale image in its blue channel.
563///
564/// # Examples
565/// ```
566/// # extern crate image;
567/// # #[macro_use]
568/// # extern crate imageproc;
569/// # fn main() {
570/// use image::Luma;
571/// use imageproc::map::as_blue_channel;
572///
573/// let image = gray_image!(
574/// 1, 2;
575/// 3, 4);
576///
577/// let expected = rgb_image!(
578/// [0, 0, 1], [0, 0, 2];
579/// [0, 0, 3], [0, 0, 4]);
580///
581/// let actual = as_blue_channel(&image);
582/// assert_pixels_eq!(actual, expected);
583/// # }
584/// ```
585pub fn as_blue_channel<I, C>(image: &I) -> Image<Rgb<C>>
586where
587 I: GenericImage<Pixel = Luma<C>>,
588 Rgb<C>: Pixel<Subpixel = C>,
589 C: Primitive,
590{
591 map_colors(image, |p| {
592 let mut cs = [C::zero(); 3];
593 cs[2] = p[0];
594 Rgb(cs)
595 })
596}