effects.go (3773B)
1 package imaging 2 3 import ( 4 "image" 5 "math" 6 ) 7 8 func 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 // 19 func 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 34 func 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 84 func 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 // 141 func 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 }