imageproc/
pixelops.rs

1//! Pixel manipulations.
2
3use crate::definitions::Clamp;
4use crate::math::cast;
5use conv::ValueInto;
6use image::Pixel;
7
8/// Adds pixels with the given weights. Results are clamped to prevent arithmetical overflows.
9///
10/// # Examples
11/// ```
12/// # extern crate image;
13/// # extern crate imageproc;
14/// # fn main() {
15/// use image::Rgb;
16/// use imageproc::pixelops::weighted_sum;
17///
18/// let left = Rgb([10u8, 20u8, 30u8]);
19/// let right = Rgb([100u8, 80u8, 60u8]);
20///
21/// let sum = weighted_sum(left, right, 0.7, 0.3);
22/// assert_eq!(sum, Rgb([37, 38, 39]));
23/// # }
24/// ```
25pub fn weighted_sum<P: Pixel>(left: P, right: P, left_weight: f32, right_weight: f32) -> P
26where
27    P::Subpixel: ValueInto<f32> + Clamp<f32>,
28{
29    left.map2(&right, |p, q| {
30        weighted_channel_sum(p, q, left_weight, right_weight)
31    })
32}
33
34/// Equivalent to `weighted_sum(left, right, left_weight, 1 - left_weight)`.
35///
36/// # Examples
37/// ```
38/// # extern crate image;
39/// # extern crate imageproc;
40/// # fn main() {
41/// use image::Rgb;
42/// use imageproc::pixelops::interpolate;
43///
44/// let left = Rgb([10u8, 20u8, 30u8]);
45/// let right = Rgb([100u8, 80u8, 60u8]);
46///
47/// let sum = interpolate(left, right, 0.7);
48/// assert_eq!(sum, Rgb([37, 38, 39]));
49/// # }
50/// ```
51pub fn interpolate<P: Pixel>(left: P, right: P, left_weight: f32) -> P
52where
53    P::Subpixel: ValueInto<f32> + Clamp<f32>,
54{
55    weighted_sum(left, right, left_weight, 1.0 - left_weight)
56}
57
58#[inline(always)]
59fn weighted_channel_sum<C>(left: C, right: C, left_weight: f32, right_weight: f32) -> C
60where
61    C: ValueInto<f32> + Clamp<f32>,
62{
63    Clamp::clamp(cast(left) * left_weight + cast(right) * right_weight)
64}
65
66#[cfg(test)]
67mod tests {
68    use super::*;
69    use image::{Luma, Rgb};
70    use test::{black_box, Bencher};
71
72    #[test]
73    fn test_weighted_channel_sum() {
74        // Midpoint
75        assert_eq!(weighted_channel_sum(10u8, 20u8, 0.5, 0.5), 15u8);
76        // Mainly left
77        assert_eq!(weighted_channel_sum(10u8, 20u8, 0.9, 0.1), 11u8);
78        // Clamped
79        assert_eq!(weighted_channel_sum(150u8, 150u8, 1.8, 0.8), 255u8);
80    }
81
82    #[bench]
83    fn bench_weighted_sum_rgb(b: &mut Bencher) {
84        b.iter(|| {
85            let left = black_box(Rgb([10u8, 20u8, 33u8]));
86            let right = black_box(Rgb([80u8, 70u8, 60u8]));
87            let left_weight = black_box(0.3);
88            let right_weight = black_box(0.7);
89            black_box(weighted_sum(left, right, left_weight, right_weight));
90        })
91    }
92
93    #[bench]
94    fn bench_weighted_sum_gray(b: &mut Bencher) {
95        b.iter(|| {
96            let left = black_box(Luma([10u8]));
97            let right = black_box(Luma([80u8]));
98            let left_weight = black_box(0.3);
99            let right_weight = black_box(0.7);
100            black_box(weighted_sum(left, right, left_weight, right_weight));
101        })
102    }
103
104    #[bench]
105    fn bench_interpolate_rgb(b: &mut Bencher) {
106        b.iter(|| {
107            let left = black_box(Rgb([10u8, 20u8, 33u8]));
108            let right = black_box(Rgb([80u8, 70u8, 60u8]));
109            let left_weight = black_box(0.3);
110            black_box(interpolate(left, right, left_weight));
111        })
112    }
113
114    #[bench]
115    fn bench_interpolate_gray(b: &mut Bencher) {
116        b.iter(|| {
117            let left = black_box(Luma([10u8]));
118            let right = black_box(Luma([80u8]));
119            let left_weight = black_box(0.3);
120            black_box(interpolate(left, right, left_weight));
121        })
122    }
123}