Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add fast blur algorithm #683

Closed
wants to merge 1 commit into from

Conversation

torfmaster
Copy link

This PR is an alternative to image-rs/image#2302 which adds the corresponding functionality in the image crate. It adds a fast approximation of Gaussian blur to this library.

It is an attempt to solve image-rs/image#986. For the discussion discussion please refer to the PR in image until it is decided where the functionality will go.

@torfmaster torfmaster mentioned this pull request Aug 13, 2024
7 tasks
@ripytide
Copy link
Member

ripytide commented Aug 14, 2024

Thanks for opening another PR in this repo, I feel it is much better placed here. I don't have much knowledge about the specific blurring algorithm but I am happy to help with the code quality and maybe adding some benchmarks.

@cospectrum
Copy link
Contributor

cospectrum commented Aug 19, 2024

Some beautification is needed

channel_num * idx + channel
}

fn my_fast_horizontal_blur<P: Primitive>(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My? :)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I renamed this to horizontal_fast_blur_half in the original PR.

/ (-4.0 * (w_l) - 4.0);
let m = f32::round(m_ideal) as usize;

let mut box_sizes: Vec<usize> = vec![];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Vec::with_capcity(n) or iter + collect

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I chose the latter. As n is usually between 3 and 6 (and in our case it is always 3) the effect is little, that's why I optimized this code for readability.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove the explicit Vec<usize> type for readability

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's already gone (in the PR to the image crate).

channel: usize,
channel_num: usize,
) -> P {
let x = min(width as isize - 1, max(0, x)) as usize;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

replace with clamp

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed (again in original PR)

/// Kovesi, P.: Fast Almost-Gaussian Filtering The Australian Pattern
/// Recognition Society Conference: DICTA 2010. December 2010. Sydney.
pub fn fast_blur<P: Pixel>(
image_buffer: &ImageBuffer<P, Vec<P::Subpixel>>,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replace with Image type alias from crate::definitions and rename arg to image or img

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • fix this once it is decided that 'fast_blurwill land inimageproc`

pub fn fast_blur<P: Pixel>(
image_buffer: &ImageBuffer<P, Vec<P::Subpixel>>,
sigma: f32,
) -> ImageBuffer<P, Vec<P::Subpixel>> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replace with Image type alias

Copy link
Author

@torfmaster torfmaster Aug 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • fix this once it is decided that fast_blur will land in imageproc

let val = P::from(val).unwrap();

out_samples[channel_idx(channel, i + j * height, channel_num)] = val;
vals[channel] = vals[channel]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The expression is soo long!

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I extracted the index into a speaking variable. I hope this makes the code more readable.

for j in 0..width {
for channel in 0..channel_num {
let val = vals[channel] / (2.0 * r as f32 + 1.0);
let val = if val < min_value {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replace entire if {} else if {} else {} with val.clamp(min_value, max_value)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

) -> Vec<P> {
let channel_size = width * height;

let mut out_samples: Vec<P> = vec![P::from(0).unwrap(); channel_size * channel_num];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need for explicit Vec type

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

/// Test blur doesn't panick when passed 0.0
fn test_fast_blur_zero() {
let image = RgbaImage::new(50, 50);
let _ = super::fast_blur(&image, -1.0);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add use super::* above

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

@@ -0,0 +1,163 @@
use std::cmp::{max, min};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All min/max function calls can be avoided

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

};
let w_u = w_l + 2.0;

let m_ideal = (12.0 * sigma * sigma
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Expression is too long, I'm sure there is a way to simplify it

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It`s II (5) in https://peterkovesi.com/papers/FastGaussianSmoothing.pdf. It results from inverting the equation of ideal sigma and the one coming from repeated box blurs. I don't know how to further simplify this polynomial as all of the terms are linearly independent (as polynomials).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let m_ideal = 0.25 * (n as f32) * (w_l + 3.0) - 3.0 * sigma.powi(2) * (w_l + 1.0).recip();

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you, now the formula fits into one line.

use image::RgbaImage;

#[test]
/// Test blur doesn't panick when passed 0.0
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's -1.0

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed already in original PR

@torfmaster
Copy link
Author

@cospectrum thank you for your review comments. I am going to address them here and in my PR on the image crate. However, as long there is no decision whether the code will land in image or in imageproc I think it makes sense to continue the review in the PR for the image crate.

@torfmaster
Copy link
Author

I just wanted to give a short status report (I also cross post this comment to #683)

  • I added a functional test showing (for an example) that the effect of fast_blur is close to the Gaussian blur implementation in this library
  • I added a benchmark that shows that fast blur is much faster than the current blur implementation
  • I think I addressed all code review comments

What would be the next steps?

@cospectrum
Copy link
Contributor

merged into image

@torfmaster
Copy link
Author

As the code is now in the image crate I will close this PR.

@torfmaster torfmaster closed this Oct 16, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants