imageproc/
noise.rs

1//! Functions for adding synthetic noise to images.
2
3use crate::definitions::{Clamp, HasBlack, HasWhite, Image};
4use crate::math::cast;
5use conv::ValueInto;
6use image::Pixel;
7use rand::{rngs::StdRng, SeedableRng};
8use rand_distr::{Distribution, Normal, Uniform};
9
10/// Adds independent additive Gaussian noise to all channels
11/// of an image, with the given mean and standard deviation.
12pub fn gaussian_noise<P>(image: &Image<P>, mean: f64, stddev: f64, seed: u64) -> Image<P>
13where
14    P: Pixel,
15    P::Subpixel: ValueInto<f64> + Clamp<f64>,
16{
17    let mut out = image.clone();
18    gaussian_noise_mut(&mut out, mean, stddev, seed);
19    out
20}
21
22/// Adds independent additive Gaussian noise to all channels
23/// of an image in place, with the given mean and standard deviation.
24pub fn gaussian_noise_mut<P>(image: &mut Image<P>, mean: f64, stddev: f64, seed: u64)
25where
26    P: Pixel,
27    P::Subpixel: ValueInto<f64> + Clamp<f64>,
28{
29    let mut rng: StdRng = SeedableRng::seed_from_u64(seed);
30    let normal = Normal::new(mean, stddev).unwrap();
31
32    for p in image.pixels_mut() {
33        for c in p.channels_mut() {
34            let noise = normal.sample(&mut rng);
35            *c = P::Subpixel::clamp(cast(*c) + noise);
36        }
37    }
38}
39
40/// Converts pixels to black or white at the given `rate` (between 0.0 and 1.0).
41/// Black and white occur with equal probability.
42pub fn salt_and_pepper_noise<P>(image: &Image<P>, rate: f64, seed: u64) -> Image<P>
43where
44    P: Pixel + HasBlack + HasWhite,
45{
46    let mut out = image.clone();
47    salt_and_pepper_noise_mut(&mut out, rate, seed);
48    out
49}
50
51/// Converts pixels to black or white in place at the given `rate` (between 0.0 and 1.0).
52/// Black and white occur with equal probability.
53pub fn salt_and_pepper_noise_mut<P>(image: &mut Image<P>, rate: f64, seed: u64)
54where
55    P: Pixel + HasBlack + HasWhite,
56{
57    let mut rng: StdRng = SeedableRng::seed_from_u64(seed);
58    let uniform = Uniform::new(0.0, 1.0);
59
60    for p in image.pixels_mut() {
61        if uniform.sample(&mut rng) > rate {
62            continue;
63        }
64        let r = uniform.sample(&mut rng);
65        *p = if r >= 0.5 { P::white() } else { P::black() };
66    }
67}
68
69#[cfg(test)]
70mod tests {
71    use super::*;
72    use image::GrayImage;
73    use test::{black_box, Bencher};
74
75    #[bench]
76    fn bench_gaussian_noise_mut(b: &mut Bencher) {
77        let mut image = GrayImage::new(100, 100);
78        b.iter(|| {
79            gaussian_noise_mut(&mut image, 30.0, 40.0, 1);
80        });
81        black_box(image);
82    }
83
84    #[bench]
85    fn bench_salt_and_pepper_noise_mut(b: &mut Bencher) {
86        let mut image = GrayImage::new(100, 100);
87        b.iter(|| {
88            salt_and_pepper_noise_mut(&mut image, 0.3, 1);
89        });
90        black_box(image);
91    }
92}