package goeffects // Package copied from https://github.com/markdaws/go-effects import "fmt" import "image" import "math" import "runtime" type gaussian struct { kernelSize int sigma float64 } // NewGaussian is an effect that applies a gaussian blur to the image func NewGaussian(kernelSize int, sigma float64) Effect { return &gaussian{ kernelSize: kernelSize, sigma: sigma, } } func (g *gaussian) Apply(img *Image, numRoutines int) (*Image, error) { if !isOddInt(g.kernelSize) { return nil, fmt.Errorf("kernel size must be odd") } if numRoutines == 0 { numRoutines = runtime.GOMAXPROCS(0) } kernel := gaussianKernel(g.kernelSize, g.sigma) kernelOffset := (g.kernelSize - 1) / 2 pf := func(ri, x, y, offset, inStride int, inPix, outPix []uint8) { var gr, gb, gg float64 for dy := -kernelOffset; dy <= kernelOffset; dy++ { for dx := -kernelOffset; dx <= kernelOffset; dx++ { pOffset := offset + (dx*4 + dy*inStride) r := inPix[pOffset] g := inPix[pOffset+1] b := inPix[pOffset+2] scale := kernel[dx+kernelOffset][dy+kernelOffset] gr += scale * float64(r) gg += scale * float64(g) gb += scale * float64(b) } } outPix[offset] = uint8(gr) outPix[offset+1] = uint8(gg) outPix[offset+2] = uint8(gb) outPix[offset+3] = 255 } out := &Image{ img: image.NewRGBA(image.Rectangle{ Min: image.Point{X: 0, Y: 0}, Max: image.Point{X: img.Width, Y: img.Height}, }), Width: img.Width, Height: img.Height, Bounds: Rect{ X: img.Bounds.X + kernelOffset, Y: img.Bounds.Y + kernelOffset, Width: img.Bounds.Width - 2*kernelOffset, Height: img.Bounds.Height - 2*kernelOffset, }, } runParallel(numRoutines, img, out.Bounds, out, pf, 0) return out, nil } func gaussianKernel(dimension int, sigma float64) [][]float64 { k := make([][]float64, dimension) sum := 0.0 for x := 0; x < dimension; x++ { k[x] = make([]float64, dimension) for y := 0; y < dimension; y++ { k[x][y] = gaussianXY(x, y, sigma) sum += k[x][y] } } scale := 1.0 / sum for y := 0; y < dimension; y++ { for x := 0; x < dimension; x++ { k[x][y] *= scale } } return k } // expects x,y to be 0 at the center of the kernel func gaussianXY(x, y int, sigma float64) float64 { return ((1.0 / (2 * math.Pi * sigma * sigma)) * math.E) - (float64(x*x+y*y) / (2 * sigma * sigma)) }