From 4cf9689ee850e57d0a8fe4c475985862bf40f227 Mon Sep 17 00:00:00 2001 From: Dirleye Date: Tue, 29 Jul 2025 15:12:09 +0100 Subject: [PATCH 1/2] Add speckle-noise function --- src/noise.rs | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/src/noise.rs b/src/noise.rs index 613211ad..338d5274 100644 --- a/src/noise.rs +++ b/src/noise.rs @@ -33,6 +33,59 @@ where } } +/// Adds multiplicative speckle noise to an image with the given mean and standard deviation. +/// Noise can be added independently per channel for data augmentation, or just per pixel for +/// realistic sensor noise simulation. +pub fn speckle_noise

( + image: &Image

, + per_channel: bool, + mean: f64, + stddev: f64, + seed: u64, +) -> Image

+where + P: Pixel, + P::Subpixel: Into + Clamp, +{ + let mut out = image.clone(); + speckle_noise_mut(&mut out, per_channel, mean, stddev, seed); + out +} +#[doc=generate_mut_doc_comment!("speckle_noise")] +pub fn speckle_noise_mut

( + image: &mut Image

, + per_channel: bool, + mean: f64, + stddev: f64, + seed: u64, +) where + P: Pixel, + P::Subpixel: Into + Clamp, +{ + let mut rng: StdRng = SeedableRng::seed_from_u64(seed); + let normal = Normal::new(mean, stddev).unwrap(); + + if per_channel { + // Add sampled noise per channel in each pixel + for p in image.pixels_mut() { + p.apply(|c| { + let noise = normal.sample(&mut rng); + let original = c.into(); + P::Subpixel::clamp(original + original * noise) + }); + } + } else { + // Use the same noise pattern for each channel + for p in image.pixels_mut() { + let noise = normal.sample(&mut rng); + p.apply(|c| { + let original = c.into(); + P::Subpixel::clamp(original + original * noise) + }); + } + } +} + /// Converts pixels to black or white at the given `rate` (between 0.0 and 1.0). /// Black and white occur with equal probability. pub fn salt_and_pepper_noise

(image: &Image

, rate: f64, seed: u64) -> Image

@@ -84,4 +137,13 @@ mod benches { }); black_box(image); } + + #[bench] + fn bench_speckle_noise_mut(b: &mut Bencher) { + let mut image = GrayImage::new(100, 100); + b.iter(|| { + speckle_noise_mut(&mut image, true, 0.0, 0.4, 1); + }); + black_box(image); + } } From 0a71b3589bf91fc397042201519a645c22b3f201 Mon Sep 17 00:00:00 2001 From: Dirleye Date: Wed, 26 Nov 2025 21:47:32 +0000 Subject: [PATCH 2/2] Split speckle noise and speckle noise per channel into different functions --- src/noise.rs | 88 +++++++++++++++++++++++++++++++--------------------- 1 file changed, 53 insertions(+), 35 deletions(-) diff --git a/src/noise.rs b/src/noise.rs index 338d5274..5ebcb04e 100644 --- a/src/noise.rs +++ b/src/noise.rs @@ -34,55 +34,64 @@ where } /// Adds multiplicative speckle noise to an image with the given mean and standard deviation. -/// Noise can be added independently per channel for data augmentation, or just per pixel for -/// realistic sensor noise simulation. -pub fn speckle_noise

( - image: &Image

, - per_channel: bool, - mean: f64, - stddev: f64, - seed: u64, -) -> Image

+/// Noise is added per pixel for realistic sensor noise simulation. +pub fn speckle_noise

(image: &Image

, mean: f64, stddev: f64, seed: u64) -> Image

where P: Pixel, P::Subpixel: Into + Clamp, { let mut out = image.clone(); - speckle_noise_mut(&mut out, per_channel, mean, stddev, seed); + speckle_noise_mut(&mut out, mean, stddev, seed); out } + #[doc=generate_mut_doc_comment!("speckle_noise")] -pub fn speckle_noise_mut

( - image: &mut Image

, - per_channel: bool, - mean: f64, - stddev: f64, - seed: u64, -) where +pub fn speckle_noise_mut

(image: &mut Image

, mean: f64, stddev: f64, seed: u64) +where P: Pixel, P::Subpixel: Into + Clamp, { let mut rng: StdRng = SeedableRng::seed_from_u64(seed); let normal = Normal::new(mean, stddev).unwrap(); - if per_channel { - // Add sampled noise per channel in each pixel - for p in image.pixels_mut() { - p.apply(|c| { - let noise = normal.sample(&mut rng); - let original = c.into(); - P::Subpixel::clamp(original + original * noise) - }); - } - } else { - // Use the same noise pattern for each channel - for p in image.pixels_mut() { + // Use the same noise pattern for each channel + for p in image.pixels_mut() { + let noise = normal.sample(&mut rng); + p.apply(|c| { + let original = c.into(); + P::Subpixel::clamp(original + original * noise) + }); + } +} + +/// Adds multiplicative speckle noise to an image with the given mean and standard deviation. +/// Noise is added independently per channel for data augmentation. +pub fn speckle_noise_per_channel

(image: &Image

, mean: f64, stddev: f64, seed: u64) -> Image

+where + P: Pixel, + P::Subpixel: Into + Clamp, +{ + let mut out = image.clone(); + speckle_noise_per_channel_mut(&mut out, mean, stddev, seed); + out +} + +#[doc=generate_mut_doc_comment!("speckle_noise_per_channel")] +pub fn speckle_noise_per_channel_mut

(image: &mut Image

, mean: f64, stddev: f64, seed: u64) +where + P: Pixel, + P::Subpixel: Into + Clamp, +{ + let mut rng: StdRng = SeedableRng::seed_from_u64(seed); + let normal = Normal::new(mean, stddev).unwrap(); + + // Add sampled noise per channel in each pixel + for p in image.pixels_mut() { + p.apply(|c| { let noise = normal.sample(&mut rng); - p.apply(|c| { - let original = c.into(); - P::Subpixel::clamp(original + original * noise) - }); - } + let original = c.into(); + P::Subpixel::clamp(original + original * noise) + }); } } @@ -142,7 +151,16 @@ mod benches { fn bench_speckle_noise_mut(b: &mut Bencher) { let mut image = GrayImage::new(100, 100); b.iter(|| { - speckle_noise_mut(&mut image, true, 0.0, 0.4, 1); + speckle_noise_mut(&mut image, 0.0, 0.4, 1); + }); + black_box(image); + } + + #[bench] + fn bench_speckle_noise_per_channel_mut(b: &mut Bencher) { + let mut image = GrayImage::new(100, 100); + b.iter(|| { + speckle_noise_per_channel_mut(&mut image, 0.0, 0.4, 1); }); black_box(image); }