【发布时间】:2021-05-19 06:41:21
【问题描述】:
我在 Rust 中实现了 stackblur algorithm(由 Mario Klingemann 编写),并重写了水平传递以使用迭代器而不是索引。但是,与 GIMP 相比,模糊需要运行两次才能达到全部强度。将半径加倍会引入光晕。
/// Performs a horizontal pass of stackblur.
/// Input is expected to be in linear RGB color space.
/// Needs to be ran twice for full effect!
pub fn blur_horiz(src: &mut [u32], width: NonZeroUsize, radius: NonZeroU8) {
let width = width.get();
let radius = u32::from(min(radius.get() | 1, 255));
let r = radius as usize;
src.chunks_exact_mut(width).for_each(|row| {
let first = *row.first().unwrap();
let mut last = *row.last().unwrap();
let mut queue_r = VecDeque::with_capacity(r);
let mut queue_g = VecDeque::with_capacity(r);
let mut queue_b = VecDeque::with_capacity(r);
// fill with left edge pixel
for v in iter::repeat(first).take(r / 2 + 1) {
queue_r.push_back(red(v));
queue_g.push_back(green(v));
queue_b.push_back(blue(v));
}
// fill with starting pixels
for v in row.iter().copied().chain(iter::repeat(last)).take(r / 2) {
queue_r.push_back(red(v));
queue_g.push_back(green(v));
queue_b.push_back(blue(v));
}
debug_assert_eq!(queue_r.len(), r);
let mut row_iter = peek_nth(row.iter_mut());
while let Some(px) = row_iter.next() {
// set pixel
*px = pixel(
queue_r.iter().sum::<u32>() / radius,
queue_g.iter().sum::<u32>() / radius,
queue_b.iter().sum::<u32>() / radius,
);
// drop left edge of kernel
let _ = queue_r.pop_front();
let _ = queue_g.pop_front();
let _ = queue_b.pop_front();
// add right edge of kernel
let next = **row_iter.peek_nth(r / 2).unwrap_or(&&mut last);
queue_r.push_back(red(next));
queue_g.push_back(green(next));
queue_b.push_back(blue(next));
}
});
}
以 radius=15 运行两次 blur_horiz 后的结果:
以 radius=30 运行一次 blur_horiz 后的结果:
【问题讨论】:
-
请注意,您实现的是常规框过滤器,而不是 stackblur(使用三角形过滤器)。
-
“发挥全部力量”是什么意思?你能澄清一下你的意思吗?
-
@CrisLuengo 与高斯模糊相比,例如GIMP。我将进行编辑以添加说明。
-
@Jmb 哦,我想这可以解释吗?由于框模糊的重复通道近似于高斯模糊。
-
Gimp 可能根据 sigma 参数而不是半径来调整其过滤器的大小。此外,您创建了一个全尺寸为“半径”的过滤器,因此您的“半径”参数实际上是过滤器的全尺寸,而不是实际半径。
标签: image-processing rust blur gaussianblur