effects.go

  1package imaging
  2
  3import (
  4	"image"
  5	"math"
  6)
  7
  8func gaussianBlurKernel(x, sigma float64) float64 {
  9	return math.Exp(-(x*x)/(2*sigma*sigma)) / (sigma * math.Sqrt(2*math.Pi))
 10}
 11
 12// Blur produces a blurred version of the image using a Gaussian function.
 13// Sigma parameter must be positive and indicates how much the image will be blurred.
 14//
 15// Example:
 16//
 17//	dstImage := imaging.Blur(srcImage, 3.5)
 18//
 19func Blur(img image.Image, sigma float64) *image.NRGBA {
 20	if sigma <= 0 {
 21		return Clone(img)
 22	}
 23
 24	radius := int(math.Ceil(sigma * 3.0))
 25	kernel := make([]float64, radius+1)
 26
 27	for i := 0; i <= radius; i++ {
 28		kernel[i] = gaussianBlurKernel(float64(i), sigma)
 29	}
 30
 31	return blurVertical(blurHorizontal(img, kernel), kernel)
 32}
 33
 34func blurHorizontal(img image.Image, kernel []float64) *image.NRGBA {
 35	src := newScanner(img)
 36	dst := image.NewNRGBA(image.Rect(0, 0, src.w, src.h))
 37	radius := len(kernel) - 1
 38
 39	parallel(0, src.h, func(ys <-chan int) {
 40		scanLine := make([]uint8, src.w*4)
 41		scanLineF := make([]float64, len(scanLine))
 42		for y := range ys {
 43			src.scan(0, y, src.w, y+1, scanLine)
 44			for i, v := range scanLine {
 45				scanLineF[i] = float64(v)
 46			}
 47			for x := 0; x < src.w; x++ {
 48				min := x - radius
 49				if min < 0 {
 50					min = 0
 51				}
 52				max := x + radius
 53				if max > src.w-1 {
 54					max = src.w - 1
 55				}
 56				var r, g, b, a, wsum float64
 57				for ix := min; ix <= max; ix++ {
 58					i := ix * 4
 59					weight := kernel[absint(x-ix)]
 60					wsum += weight
 61					s := scanLineF[i : i+4 : i+4]
 62					wa := s[3] * weight
 63					r += s[0] * wa
 64					g += s[1] * wa
 65					b += s[2] * wa
 66					a += wa
 67				}
 68				if a != 0 {
 69					aInv := 1 / a
 70					j := y*dst.Stride + x*4
 71					d := dst.Pix[j : j+4 : j+4]
 72					d[0] = clamp(r * aInv)
 73					d[1] = clamp(g * aInv)
 74					d[2] = clamp(b * aInv)
 75					d[3] = clamp(a / wsum)
 76				}
 77			}
 78		}
 79	})
 80
 81	return dst
 82}
 83
 84func blurVertical(img image.Image, kernel []float64) *image.NRGBA {
 85	src := newScanner(img)
 86	dst := image.NewNRGBA(image.Rect(0, 0, src.w, src.h))
 87	radius := len(kernel) - 1
 88
 89	parallel(0, src.w, func(xs <-chan int) {
 90		scanLine := make([]uint8, src.h*4)
 91		scanLineF := make([]float64, len(scanLine))
 92		for x := range xs {
 93			src.scan(x, 0, x+1, src.h, scanLine)
 94			for i, v := range scanLine {
 95				scanLineF[i] = float64(v)
 96			}
 97			for y := 0; y < src.h; y++ {
 98				min := y - radius
 99				if min < 0 {
100					min = 0
101				}
102				max := y + radius
103				if max > src.h-1 {
104					max = src.h - 1
105				}
106				var r, g, b, a, wsum float64
107				for iy := min; iy <= max; iy++ {
108					i := iy * 4
109					weight := kernel[absint(y-iy)]
110					wsum += weight
111					s := scanLineF[i : i+4 : i+4]
112					wa := s[3] * weight
113					r += s[0] * wa
114					g += s[1] * wa
115					b += s[2] * wa
116					a += wa
117				}
118				if a != 0 {
119					aInv := 1 / a
120					j := y*dst.Stride + x*4
121					d := dst.Pix[j : j+4 : j+4]
122					d[0] = clamp(r * aInv)
123					d[1] = clamp(g * aInv)
124					d[2] = clamp(b * aInv)
125					d[3] = clamp(a / wsum)
126				}
127			}
128		}
129	})
130
131	return dst
132}
133
134// Sharpen produces a sharpened version of the image.
135// Sigma parameter must be positive and indicates how much the image will be sharpened.
136//
137// Example:
138//
139//	dstImage := imaging.Sharpen(srcImage, 3.5)
140//
141func Sharpen(img image.Image, sigma float64) *image.NRGBA {
142	if sigma <= 0 {
143		return Clone(img)
144	}
145
146	src := newScanner(img)
147	dst := image.NewNRGBA(image.Rect(0, 0, src.w, src.h))
148	blurred := Blur(img, sigma)
149
150	parallel(0, src.h, func(ys <-chan int) {
151		scanLine := make([]uint8, src.w*4)
152		for y := range ys {
153			src.scan(0, y, src.w, y+1, scanLine)
154			j := y * dst.Stride
155			for i := 0; i < src.w*4; i++ {
156				val := int(scanLine[i])<<1 - int(blurred.Pix[j])
157				if val < 0 {
158					val = 0
159				} else if val > 0xff {
160					val = 0xff
161				}
162				dst.Pix[j] = uint8(val)
163				j++
164			}
165		}
166	})
167
168	return dst
169}