Home > Article > Backend Development > Gaussian blur implementation produces strange output
php Xiaobian Yuzai pointed out that Gaussian blur is a common image processing technology that can blur images and is often used to beautify photos or achieve special effects. However, if the Gaussian blur algorithm is not implemented correctly, it can produce strange output. This may include issues such as image distortion, blurred edges, or color shifts. Therefore, when using Gaussian blur technology, attention must be paid to the correct implementation of the algorithm to ensure the expected output effect.
I am trying to implement Gaussian blur on golang image.image
object. For the following images:
The generated output image is:
As one can see, the output image contains some unhandled boundaries that correspond to the current implementation decision not to handle edges, which makes me think that I may have messed up the calculation in some way (I mean Yes, this part of the implementation works so I can discard the off-by-one error when iterating over image pixels). I've checked this code multiple times but I can't find my error. I'd really appreciate some help and considerations on implementation that could help me figure it out. The code is included below. If any edits or clarifications are needed, please let me know!
package main import ( "image" "image/color" "image/draw" "image/jpeg" "math" "os" ) func main() { f, err := os.Open("dog.jpeg") if err != nil { panic(err) } img, err := jpeg.Decode(f) if err != nil { panic(err) } newImg := gaussianBlur(img, 3) out, err := os.Create("dog-blurred.jpeg") if err != nil { panic(err) } err = jpeg.Encode(out, newImg, nil) if err != nil { panic(err) } } func applyGaussianFunction(x, y, stdDev float64) float64 { // eFactor := 1 / (2 * math.Pi * stdDev*stdDev); ePowNominator := -(x*x + y*y); ePowDenominator := 2 * stdDev*stdDev; return math.Pow(math.E, (ePowNominator/ePowDenominator)); } func generateKernel(radius int) [][]float64 { size := 1 + (radius * 2); kernel := make([][]float64, size); stdDev := math.Max(float64(radius / 2), 1); sum := float64(0); for i := 0; i < size; i++ { kernel[i] = make([]float64, size); } for i := -radius; i < radius + 1; i++ { for j := -radius; j < radius + 1; j++ { val := applyGaussianFunction(float64(j), float64(i), stdDev); kernel[i + radius][j + radius] = val; sum += val; } } for i := 0; i < size; i++ { for j := 0; j < size; j++ { kernel[i][j] /= sum; } } return kernel; } func makeImageRGBA(src image.Image) *image.RGBA { b := src.Bounds().Size(); rgba := image.NewRGBA(image.Rect(0, 0, b.X, b.Y)); draw.Draw(rgba, rgba.Bounds(), src, image.Pt(0, 0), draw.Src); return rgba; } func gaussianBlur(img image.Image, radius int) image.Image { size := img.Bounds().Size(); rgbaImg := image.NewRGBA(image.Rect(0, 0, size.X, size.Y)); kernel := generateKernel(radius); for y := radius; y < size.Y - radius; y++ { for x := radius; x < size.X - radius; x++ { var nr, ng, nb, na float64 = 0, 0, 0, 0; for i := -radius; i < radius + 1; i++ { for j := -radius; j < radius + 1; j++ { // NEW: Get pixels from original Image pr, pg, pb, pa := img.At(x - j, y - i).RGBA(); nr += float64(pr) * kernel[i + radius][j + radius]; ng += float64(pg) * kernel[i + radius][j + radius]; nb += float64(pb) * kernel[i + radius][j + radius]; na += float64(pa) * kernel[i + radius][j + radius]; } } // Handle overflow by using 64-bit alphapremultiplied values rgbaImg.Set(x, y, color.RGBA64{uint16(nr), uint16(ng), uint16(nb), uint16(na)}); } } return rgbaImg; }
edit
rgbaimg
efactor
from the applygaussianfunction
function since I already used the sum
variable to normalize the kernel.set
method to use 64-bit rgba structureThis is the newly generated image
Those black borders are easy to fix, I'm already working on them. This is no longer part of the problem.
You are reading from the same image that you are writing to. You should read from the original image:
pr, pg, pb, pa := img.at(x+j, y+i).rgba()
edit:
Additionally, image.at
returns color.rgba
, while func (color.rgba) rgba
returns the range from 0 to 0xffff. However the color.rgba
constructor expects them to be in the range 0 to 255. You may want to use color.rgba64
when writing the results:
rgbaImg.Set(x, y, color.RGBA64{uint16(nr), uint16(ng), uint16(nb), uint16(na)});
The above is the detailed content of Gaussian blur implementation produces strange output. For more information, please follow other related articles on the PHP Chinese website!