image/imageops/
fast_blur.rs1use num_traits::clamp;
2
3use crate::{ImageBuffer, Pixel, Primitive};
4
5pub fn fast_blur<P: Pixel>(
9 image_buffer: &ImageBuffer<P, Vec<P::Subpixel>>,
10 sigma: f32,
11) -> ImageBuffer<P, Vec<P::Subpixel>> {
12 let (width, height) = image_buffer.dimensions();
13
14 if width == 0 || height == 0 {
15 return image_buffer.clone();
16 }
17 let mut samples = image_buffer.as_flat_samples().samples.to_vec();
18 let num_passes = 3;
19
20 let boxes = boxes_for_gauss(sigma, num_passes);
21
22 for radius in boxes.iter().take(num_passes) {
23 let horizontally_blurred_transposed = horizontal_fast_blur_half::<P::Subpixel>(
24 &samples,
25 width as usize,
26 height as usize,
27 (*radius - 1) / 2,
28 P::CHANNEL_COUNT as usize,
29 );
30 samples = horizontal_fast_blur_half::<P::Subpixel>(
31 &horizontally_blurred_transposed,
32 height as usize,
33 width as usize,
34 (*radius - 1) / 2,
35 P::CHANNEL_COUNT as usize,
36 );
37 }
38 ImageBuffer::from_raw(width, height, samples).unwrap()
39}
40
41fn boxes_for_gauss(sigma: f32, n: usize) -> Vec<usize> {
42 let w_ideal = f32::sqrt((12.0 * sigma.powi(2) / (n as f32)) + 1.0);
43 let mut w_l = w_ideal.floor();
44 if w_l % 2.0 == 0.0 {
45 w_l -= 1.0
46 };
47 let w_u = w_l + 2.0;
48
49 let m_ideal = 0.25 * (n as f32) * (w_l + 3.0) - 3.0 * sigma.powi(2) * (w_l + 1.0).recip();
50
51 let m = f32::round(m_ideal) as usize;
52
53 (0..n)
54 .map(|i| if i < m { w_l as usize } else { w_u as usize })
55 .collect::<Vec<_>>()
56}
57
58fn channel_idx(channel: usize, idx: usize, channel_num: usize) -> usize {
59 channel_num * idx + channel
60}
61
62fn horizontal_fast_blur_half<P: Primitive>(
63 samples: &[P],
64 width: usize,
65 height: usize,
66 r: usize,
67 channel_num: usize,
68) -> Vec<P> {
69 let channel_size = width * height;
70
71 let mut out_samples = vec![P::from(0).unwrap(); channel_size * channel_num];
72 let mut vals = vec![0.0; channel_num];
73
74 let min_value = P::DEFAULT_MIN_VALUE.to_f32().unwrap();
75 let max_value = P::DEFAULT_MAX_VALUE.to_f32().unwrap();
76
77 for row in 0..height {
78 for (channel, value) in vals.iter_mut().enumerate().take(channel_num) {
79 *value = ((-(r as isize))..(r + 1) as isize)
80 .map(|x| {
81 extended_f(
82 samples,
83 width,
84 height,
85 x,
86 row as isize,
87 channel,
88 channel_num,
89 )
90 .to_f32()
91 .unwrap_or(0.0)
92 })
93 .sum()
94 }
95
96 for column in 0..width {
97 for (channel, channel_val) in vals.iter_mut().enumerate() {
98 let val = *channel_val / (2.0 * r as f32 + 1.0);
99 let val = clamp(val, min_value, max_value);
100 let val = P::from(val).unwrap();
101
102 let destination_row = column;
103 let destination_column = row;
104 let destination_sample_index = channel_idx(
105 channel,
106 destination_column + destination_row * height,
107 channel_num,
108 );
109 out_samples[destination_sample_index] = val;
110 *channel_val = *channel_val
111 - extended_f(
112 samples,
113 width,
114 height,
115 column as isize - r as isize,
116 row as isize,
117 channel,
118 channel_num,
119 )
120 .to_f32()
121 .unwrap_or(0.0)
122 + extended_f(
123 samples,
124 width,
125 height,
126 { column + r + 1 } as isize,
127 row as isize,
128 channel,
129 channel_num,
130 )
131 .to_f32()
132 .unwrap_or(0.0)
133 }
134 }
135 }
136
137 out_samples
138}
139
140fn extended_f<P: Primitive>(
141 samples: &[P],
142 width: usize,
143 height: usize,
144 x: isize,
145 y: isize,
146 channel: usize,
147 channel_num: usize,
148) -> P {
149 let x = clamp(x, 0, width as isize - 1) as usize;
150 let y = clamp(y, 0, height as isize - 1) as usize;
151 samples[channel_idx(channel, y * width + x, channel_num)]
152}