Skip to main content

imageproc/
noise.rs

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