123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148 |
- package imaging
- import (
- "image"
- )
- // ConvolveOptions are convolution parameters.
- type ConvolveOptions struct {
- // If Normalize is true the kernel is normalized before convolution.
- Normalize bool
- // If Abs is true the absolute value of each color channel is taken after convolution.
- Abs bool
- // Bias is added to each color channel value after convolution.
- Bias int
- }
- // Convolve3x3 convolves the image with the specified 3x3 convolution kernel.
- // Default parameters are used if a nil *ConvolveOptions is passed.
- func Convolve3x3(img image.Image, kernel [9]float64, options *ConvolveOptions) *image.NRGBA {
- return convolve(img, kernel[:], options)
- }
- // Convolve5x5 convolves the image with the specified 5x5 convolution kernel.
- // Default parameters are used if a nil *ConvolveOptions is passed.
- func Convolve5x5(img image.Image, kernel [25]float64, options *ConvolveOptions) *image.NRGBA {
- return convolve(img, kernel[:], options)
- }
- func convolve(img image.Image, kernel []float64, options *ConvolveOptions) *image.NRGBA {
- src := toNRGBA(img)
- w := src.Bounds().Max.X
- h := src.Bounds().Max.Y
- dst := image.NewNRGBA(image.Rect(0, 0, w, h))
- if w < 1 || h < 1 {
- return dst
- }
- if options == nil {
- options = &ConvolveOptions{}
- }
- if options.Normalize {
- normalizeKernel(kernel)
- }
- type coef struct {
- x, y int
- k float64
- }
- var coefs []coef
- var m int
- switch len(kernel) {
- case 9:
- m = 1
- case 25:
- m = 2
- }
- i := 0
- for y := -m; y <= m; y++ {
- for x := -m; x <= m; x++ {
- if kernel[i] != 0 {
- coefs = append(coefs, coef{x: x, y: y, k: kernel[i]})
- }
- i++
- }
- }
- parallel(0, h, func(ys <-chan int) {
- for y := range ys {
- for x := 0; x < w; x++ {
- var r, g, b float64
- for _, c := range coefs {
- ix := x + c.x
- if ix < 0 {
- ix = 0
- } else if ix >= w {
- ix = w - 1
- }
- iy := y + c.y
- if iy < 0 {
- iy = 0
- } else if iy >= h {
- iy = h - 1
- }
- off := iy*src.Stride + ix*4
- s := src.Pix[off : off+3 : off+3]
- r += float64(s[0]) * c.k
- g += float64(s[1]) * c.k
- b += float64(s[2]) * c.k
- }
- if options.Abs {
- if r < 0 {
- r = -r
- }
- if g < 0 {
- g = -g
- }
- if b < 0 {
- b = -b
- }
- }
- if options.Bias != 0 {
- r += float64(options.Bias)
- g += float64(options.Bias)
- b += float64(options.Bias)
- }
- srcOff := y*src.Stride + x*4
- dstOff := y*dst.Stride + x*4
- d := dst.Pix[dstOff : dstOff+4 : dstOff+4]
- d[0] = clamp(r)
- d[1] = clamp(g)
- d[2] = clamp(b)
- d[3] = src.Pix[srcOff+3]
- }
- }
- })
- return dst
- }
- func normalizeKernel(kernel []float64) {
- var sum, sumpos float64
- for i := range kernel {
- sum += kernel[i]
- if kernel[i] > 0 {
- sumpos += kernel[i]
- }
- }
- if sum != 0 {
- for i := range kernel {
- kernel[i] /= sum
- }
- } else if sumpos != 0 {
- for i := range kernel {
- kernel[i] /= sumpos
- }
- }
- }
|