From 3695a6ef5935c1e73666144bd568ab40e9265921 Mon Sep 17 00:00:00 2001 From: Thomas Friedel Date: Mon, 13 May 2019 11:56:36 +0200 Subject: [PATCH] improved box blur accuracy --- backend/softwarebackend/blur.go | 142 ++++++++++++++++++++++++++++++-- 1 file changed, 135 insertions(+), 7 deletions(-) diff --git a/backend/softwarebackend/blur.go b/backend/softwarebackend/blur.go index 2038472..dde33e5 100644 --- a/backend/softwarebackend/blur.go +++ b/backend/softwarebackend/blur.go @@ -18,14 +18,142 @@ func (b *SoftwareBackend) drawBlurred(size float64) { draw.Draw(b.Image, b.Image.Rect, blurred, image.ZP, draw.Over) } +/* +func gauss(img *image.RGBA, size float64) *image.RGBA { + const maxDist = 128 + + var buf [2*maxDist + 1]float64 + kernel := buf[:] + gaussianKernel(size, kernel) + + dist := maxDist + for x := 0; x < maxDist; x++ { + if kernel[x] < 0.002 { + continue + } + dist = maxDist - x + kernel = kernel[x : len(kernel)-x] + break + } + + fmt.Println(dist) + + img = gaussx(img, kernel, dist) + return gaussy(img, kernel, dist) +} + +func gaussx(img *image.RGBA, kernel []float64, maxDist int) *image.RGBA { + bounds := img.Bounds() + result := image.NewRGBA(bounds) + w, h := bounds.Dx(), bounds.Dy() + + for y := 0; y < h; y++ { + for x := 0; x < w; x++ { + imgFrom, imgTo := x-maxDist, x+maxDist + kernelFrom := 0 + if imgFrom < 0 { + kernelFrom -= imgFrom + imgFrom = 0 + } + if imgTo >= w { + imgTo = w - 1 + } + kernelSum := 0.0 + var r, g, b, a float64 + for xx, j := imgFrom, kernelFrom; xx <= imgTo; xx++ { + k := kernel[j] + kernelSum += k + col := img.RGBAAt(xx, y) + r += float64(col.R) * k + g += float64(col.G) * k + b += float64(col.B) * k + a += float64(col.A) * k + j++ + } + col := color.RGBA{ + R: uint8(math.Round(r / kernelSum)), + G: uint8(math.Round(g / kernelSum)), + B: uint8(math.Round(b / kernelSum)), + A: uint8(math.Round(a / kernelSum)), + } + result.SetRGBA(x, y, col) + } + } + + return result +} + +func gaussy(img *image.RGBA, kernel []float64, maxDist int) *image.RGBA { + bounds := img.Bounds() + result := image.NewRGBA(bounds) + w, h := bounds.Dx(), bounds.Dy() + + for y := 0; y < h; y++ { + imgFrom, imgTo := y-maxDist, y+maxDist + kernelFrom := 0 + if imgFrom < 0 { + kernelFrom -= imgFrom + imgFrom = 0 + } + if imgTo >= h { + imgTo = h - 1 + } + for x := 0; x < w; x++ { + kernelSum := 0.0 + var r, g, b, a float64 + for yy, j := imgFrom, kernelFrom; yy <= imgTo; yy++ { + k := kernel[j] + kernelSum += k + col := img.RGBAAt(x, yy) + r += float64(col.R) * k + g += float64(col.G) * k + b += float64(col.B) * k + a += float64(col.A) * k + j++ + } + col := color.RGBA{ + R: uint8(math.Round(r / kernelSum)), + G: uint8(math.Round(g / kernelSum)), + B: uint8(math.Round(b / kernelSum)), + A: uint8(math.Round(a / kernelSum)), + } + result.SetRGBA(x, y, col) + } + } + + return result +} + +func gaussianKernel(stddev float64, target []float64) { + stddevSqr := stddev * stddev + center := float64(len(target) / 2) + factor := 1.0 / math.Sqrt(2*math.Pi*stddevSqr) + for i := range target { + x := float64(i) - center + target[i] = factor * math.Pow(math.E, -x*x/(2*stddevSqr)) + } +} +*/ + func box3(img *image.RGBA, size float64) *image.RGBA { - rsize := int(math.Round(size * 0.75)) // todo properly size - img = box3x(img, rsize) - img = box3x(img, rsize) - img = box3x(img, rsize) - img = box3y(img, rsize) - img = box3y(img, rsize) - img = box3y(img, rsize) + size *= 1 - 1/(size+1) // this just seems to improve the accuracy + + fsize := math.Floor(size) + sizea := int(fsize) + sizeb := sizea + sizec := sizea + if size-fsize > 0.333333333 { + sizeb++ + } + if size-fsize > 0.666666666 { + sizec++ + } + img = box3x(img, sizea) + img = box3x(img, sizeb) + img = box3x(img, sizec) + img = box3y(img, sizea) + img = box3y(img, sizeb) + img = box3y(img, sizec) return img }