php小編魚仔指出,高斯模糊是一種常用的圖像處理技術(shù),可以使圖像變得模糊,常用于美化照片或?qū)崿F(xiàn)特效。然而,如果不正確地實(shí)現(xiàn)高斯模糊算法,可能會產(chǎn)生奇怪的輸出。這可能包括圖像失真、邊緣模糊或色彩偏移等問題。因此,在使用高斯模糊技術(shù)時,必須注意算法的正確實(shí)現(xiàn),以確保獲得預(yù)期的輸出效果。
問題內(nèi)容
我正在嘗試在 golang image.image
對象上實(shí)現(xiàn)高斯模糊。對于以下圖像:
生成的輸出圖像為:
正如人們所看到的,輸出圖像包含一些未處理的邊界,這些邊界對應(yīng)于當(dāng)前不處理邊緣的實(shí)現(xiàn)決策,這讓我認(rèn)為我可能在某種程度上搞砸了計算(我的意思是,這部分實(shí)現(xiàn)的工作原理,因此我可以在迭代圖像像素時丟棄差一錯誤)。我已經(jīng)多次檢查了這段代碼,但我找不到我的錯誤。我非常感謝有關(guān)實(shí)施的一些幫助和考慮,這可以幫助我解決問題。代碼包含在下面。如果需要任何編輯或澄清,請告訴我!
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; }
登錄后復(fù)制
編輯
我修改了代碼,以便從原始圖像中讀取像素,而不是從 rgbaimg
我還從 applygaussianfunction
函數(shù)注釋了 efactor
,因?yàn)槲乙呀?jīng)使用 sum
變量規(guī)范化內(nèi)核
修改了 .set
方法以使用 64 位 rgba 結(jié)構(gòu)
這是新生成的圖像
那些黑色邊框很容易解決,我已經(jīng)在解決它們了。這不再是問題的一部分。
解決方法
您正在從正在寫入的同一圖像中讀取內(nèi)容。您應(yīng)該從原始圖像中讀取:
pr, pg, pb, pa := img.at(x+j, y+i).rgba()
登錄后復(fù)制
編輯:
此外, image.at
返回 color.rgba
,而 func (color.rgba) rgba
返回0 到 0xffff 范圍。然而 color.rgba
構(gòu)造函數(shù)期望它們在 0 到 255 范圍內(nèi)。在寫入結(jié)果時,您可能需要使用 color.rgba64
:
rgbaImg.Set(x, y, color.RGBA64{uint16(nr), uint16(ng), uint16(nb), uint16(na)});
登錄后復(fù)制