seekia/imported/goeffects/gaussian.go

100 lines
2.3 KiB
Go

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))
}