为什么我的高斯模糊近似强度是一半?
Why is my gaussian blur approximation half strength?
我已经在 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));
}
});
}
运行宁blur_horiz两次后半径=15的结果:
运行宁blur_horiz一次半径=30后的结果:
请注意,您实现的是常规框过滤器,而不是 stackblur(它使用三角形过滤器)。另外,用一个半径为R
的盒子过滤两次相当于用一个半径为2*R
的三角形过滤一次,这就解释了为什么在运行 blur_horiz
两次时得到预期的结果.
我已经在 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));
}
});
}
运行宁blur_horiz两次后半径=15的结果:
运行宁blur_horiz一次半径=30后的结果:
请注意,您实现的是常规框过滤器,而不是 stackblur(它使用三角形过滤器)。另外,用一个半径为R
的盒子过滤两次相当于用一个半径为2*R
的三角形过滤一次,这就解释了为什么在运行 blur_horiz
两次时得到预期的结果.