You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Mutating an image with a Gaussian blur yields a blur that isn't quite Gaussian, much more closely resembling a box blur. I implemented a mathematically accurate Gaussian blur that factors in every pixel in the image (obviously overkill and inefficient in a 0-255 color space, but completely correct), and the results are as follows:
Note the similarity between ImageSharp's Gaussian blur and its box blur.
I would fix this myself and submit a PR but I genuinely have no clue why this happens. All of the processing regarding the generation of the kernel and the underlying Gaussian function seems to be correct and yields the same values as my own implementation.
Notes: Using ImageSharp v1.0.0-beta0005 on .NET Framework 4.7.1 on a Windows 10 64-bit OS.
Can you do me a favour and compare against the latest dev build? We’ve updated the convolution code there so it would be good to see if anything has changed.
Certainly, but it's messy and doesn't use any of ImageSharp's DenseMatrix or convolution features.
internalclassMathematicalGaussianBlurProcessor:IImageProcessor<Rgba32>{publicfloatSigma{get;}publicMathematicalGaussianBlurProcessor(floatsigma)=>this.Sigma=sigma;/// <summary>A function in the form ae^(-(x - b)² / 2c²).</summary>/// <param name="a">The height of the curve's peak.</param>/// <param name="b">The position of the center of the curve's peak (or the mean in a normal distribution).</param>/// <param name="c">The standard deviation of the curve, affecting the width of the curve.</param>privatedoubleGaussianFunction(doublex,doublea,doubleb,doublec)=>a*Math.Exp(-(x-b)*(x-b)/(2*c*c));publicvoidApply(Image<Rgba32>source,RectanglesourceRectangle){// Generate a table for the sake of speed.// The table will only need to be as large as the largest dimension of the image because the Gaussian function is symmetrical.double[]gaussianTable=newdouble[Math.Max(source.Width,source.Height)];for(inti=0;i<gaussianTable.Length;i++){// a is 1 because the weights will be normalized to sum to 1 when convoluting anyway.// The normalization will differ depending on the actual values so pre-calculating it is useless.gaussianTable[i]=this.GaussianFunction(i,1,0,this.Sigma);}// Clone the source for read-only access to the original pixels.Image<Rgba32>input=source.Clone();Process(input,source,gaussianTable,false);// Apply the convolution again in the horizontal direction.// This is possible because the Gaussian function is directionally seperable.input=source.Clone();Process(input,source,gaussianTable,true);}privatevoidProcess(Image<Rgba32>input,Image<Rgba32>output,double[]gaussianTable,boolhorizontal){intiSize=horizontal?input.Height:input.Width;intjSize=horizontal?input.Width:input.Height;for(inti=0;i<iSize;i++){for(intj=0;j<jSize;j++){// Doubles are used instead of floats for accuracy.doubleweightSum=0;doubler=0;doubleg=0;doubleb=0;doublea=0;for(intk=0;k<jSize;k++){// Reflect k along i.doubleweight=gaussianTable[Math.Abs(j-k)];weightSum+=weight;Rgba32pixel;if(horizontal){pixel=input[k,i];}else{pixel=input[i,k];}r+=weight*pixel.R;g+=weight*pixel.G;b+=weight*pixel.B;a+=weight*pixel.A;}// Normalize all weights such that they sum to 1, ergo the channel value is normalized to 255.// Make sure to round, otherwise there will be intense noise from floating point rounding errors.output[i,j]=newRgba32((byte)Math.Round(r/weightSum),(byte)Math.Round(g/weightSum),(byte)Math.Round(b/weightSum),(byte)Math.Round(a/weightSum));}}}}
Again, this doesn't use a kernel, instead factoring every relevant pixel.
Also note that the Gaussian function in my implementation is different than that of ImageSharp's. Compare the two:
vs.
ImageSharp's special a calculation yields a probability density function which has an area of 1, but this is negated in this particular use of the Gaussian function thanks to the normalization (see here for comparison).
Reading through your code it looks to me that the difference is simply the sampling radius per pixel. You sample the entire image while we sample (2 * sigma) + 1.
Btw I'm not very good at maths so you lost me with the graphs.
I do sample the entire image, but that's not what yields the difference in output—using the Gaussian blur function in an application like Photoshop or Paint.NET yields a similar result to mine. It appears that the shape of the actual kernel is different despite what the code seems to output.
It appears that the shape of the actual kernel is different despite what the code seems to output.
I think you're correct. The calculated kernel dimensions are too small. I rewrote the kernel generation code based on the guide here http://chemaguerra.com/gaussian-filter-radius/ to preserve a minimum kernel radius of Ceiling(sigma * 3) and here is the result.
@JimBobSquarePants I have a similar problem, the blur in the browser looks completely different.
image.Image.Mutate(x => x.GaussianBlur(20));
css filter: blur(20px);
In the browser, the effect looks smoother.
I have the latest version, is there any solution for this?
Edit:
I found the problem, I used the webp file format.
Webp reduces file size by 10 times. Is it possible to apply an effect so that the blur quality is nice and the file weighs less?
Unfortunately, mobile devices have a blur performance problem at 20px.
Activity
JimBobSquarePants commentedon Dec 10, 2018
Hi there,
Can you do me a favour and compare against the latest dev build? We’ve updated the convolution code there so it would be good to see if anything has changed.
Cheers!
lemoncove commentedon Dec 10, 2018
Unfortunately, results seem to be identical in v1.0.0-dev002237.
JimBobSquarePants commentedon Dec 10, 2018
Would it be possible to share your code?
lemoncove commentedon Dec 10, 2018
Certainly, but it's messy and doesn't use any of ImageSharp's DenseMatrix or convolution features.
Again, this doesn't use a kernel, instead factoring every relevant pixel.
Also note that the Gaussian function in my implementation is different than that of ImageSharp's. Compare the two:
ImageSharp's special
acalculation yields a probability density function which has an area of 1, but this is negated in this particular use of the Gaussian function thanks to the normalization (see here for comparison).JimBobSquarePants commentedon Dec 12, 2018
Thanks for that, appreciate it..
Reading through your code it looks to me that the difference is simply the sampling radius per pixel. You sample the entire image while we sample (2 * sigma) + 1.
Btw I'm not very good at maths so you lost me with the graphs.This was a good read and helped make sense of what you are saying. http://dev.theomader.com/gaussian-kernel-calculator/
iamcarbon commentedon Dec 12, 2018
What does SkiaSharp / CSS do?
JimBobSquarePants commentedon Dec 12, 2018
CSS makes an absolute meal of it, oversampling edge pixels but produces a similar blur to @Poyo-SSB method.
lemoncove commentedon Dec 12, 2018
I do sample the entire image, but that's not what yields the difference in output—using the Gaussian blur function in an application like Photoshop or Paint.NET yields a similar result to mine. It appears that the shape of the actual kernel is different despite what the code seems to output.
JimBobSquarePants commentedon Dec 12, 2018
I think you're correct. The calculated kernel dimensions are too small. I rewrote the kernel generation code based on the guide here http://chemaguerra.com/gaussian-filter-radius/ to preserve a minimum kernel radius of
Ceiling(sigma * 3)and here is the result.lemoncove commentedon Dec 12, 2018
Ah, brilliant! Looks like that's the problem solved, then.
JimBobSquarePants commentedon Dec 12, 2018
I’ll create a PR shortly
Use correct Kernel dimensions and optimize. Fix #786
Merge pull request #789 from SixLabors/js/fix-gaussian
Alerinos commentedon Feb 11, 2023
@JimBobSquarePants I have a similar problem, the blur in the browser looks completely different.
css
filter: blur(20px);In the browser, the effect looks smoother.
I have the latest version, is there any solution for this?
Edit:
I found the problem, I used the webp file format.
Webp reduces file size by 10 times. Is it possible to apply an effect so that the blur quality is nice and the file weighs less?
Unfortunately, mobile devices have a blur performance problem at 20px.