| package imaging |
| |
| import ( |
| "image" |
| "math" |
| ) |
| |
| func gaussianBlurKernel(x, sigma float64) float64 { |
| return math.Exp(-(x*x)/(2*sigma*sigma)) / (sigma * math.Sqrt(2*math.Pi)) |
| } |
| |
| // Blur produces a blurred version of the image using a Gaussian function. |
| // Sigma parameter must be positive and indicates how much the image will be blurred. |
| // |
| // Usage example: |
| // |
| // dstImage := imaging.Blur(srcImage, 3.5) |
| // |
| func Blur(img image.Image, sigma float64) *image.NRGBA { |
| if sigma <= 0 { |
| return Clone(img) |
| } |
| |
| radius := int(math.Ceil(sigma * 3.0)) |
| kernel := make([]float64, radius+1) |
| |
| for i := 0; i <= radius; i++ { |
| kernel[i] = gaussianBlurKernel(float64(i), sigma) |
| } |
| |
| return blurVertical(blurHorizontal(img, kernel), kernel) |
| } |
| |
| func blurHorizontal(img image.Image, kernel []float64) *image.NRGBA { |
| src := newScanner(img) |
| dst := image.NewNRGBA(image.Rect(0, 0, src.w, src.h)) |
| radius := len(kernel) - 1 |
| |
| parallel(0, src.h, func(ys <-chan int) { |
| scanLine := make([]uint8, src.w*4) |
| scanLineF := make([]float64, len(scanLine)) |
| for y := range ys { |
| src.scan(0, y, src.w, y+1, scanLine) |
| for i, v := range scanLine { |
| scanLineF[i] = float64(v) |
| } |
| for x, idx := 0, 0; x < src.w; x, idx = x+1, idx+4 { |
| min := x - radius |
| if min < 0 { |
| min = 0 |
| } |
| max := x + radius |
| if max > src.w-1 { |
| max = src.w - 1 |
| } |
| |
| var r, g, b, a, wsum float64 |
| for ix := min; ix <= max; ix++ { |
| i := ix * 4 |
| weight := kernel[absint(x-ix)] |
| wsum += weight |
| wa := scanLineF[i+3] * weight |
| r += scanLineF[i+0] * wa |
| g += scanLineF[i+1] * wa |
| b += scanLineF[i+2] * wa |
| a += wa |
| } |
| if a != 0 { |
| r /= a |
| g /= a |
| b /= a |
| } |
| |
| scanLine[idx+0] = clamp(r) |
| scanLine[idx+1] = clamp(g) |
| scanLine[idx+2] = clamp(b) |
| scanLine[idx+3] = clamp(a / wsum) |
| } |
| copy(dst.Pix[y*dst.Stride:], scanLine) |
| } |
| }) |
| |
| return dst |
| } |
| |
| func blurVertical(img image.Image, kernel []float64) *image.NRGBA { |
| src := newScanner(img) |
| dst := image.NewNRGBA(image.Rect(0, 0, src.w, src.h)) |
| radius := len(kernel) - 1 |
| |
| parallel(0, src.w, func(xs <-chan int) { |
| scanLine := make([]uint8, src.h*4) |
| scanLineF := make([]float64, len(scanLine)) |
| for x := range xs { |
| src.scan(x, 0, x+1, src.h, scanLine) |
| for i, v := range scanLine { |
| scanLineF[i] = float64(v) |
| } |
| for y := 0; y < src.h; y++ { |
| min := y - radius |
| if min < 0 { |
| min = 0 |
| } |
| max := y + radius |
| if max > src.h-1 { |
| max = src.h - 1 |
| } |
| |
| var r, g, b, a, wsum float64 |
| for iy := min; iy <= max; iy++ { |
| i := iy * 4 |
| weight := kernel[absint(y-iy)] |
| wsum += weight |
| wa := scanLineF[i+3] * weight |
| r += scanLineF[i+0] * wa |
| g += scanLineF[i+1] * wa |
| b += scanLineF[i+2] * wa |
| a += wa |
| } |
| if a != 0 { |
| r /= a |
| g /= a |
| b /= a |
| } |
| |
| j := y*dst.Stride + x*4 |
| dst.Pix[j+0] = clamp(r) |
| dst.Pix[j+1] = clamp(g) |
| dst.Pix[j+2] = clamp(b) |
| dst.Pix[j+3] = clamp(a / wsum) |
| } |
| } |
| }) |
| |
| return dst |
| } |
| |
| // Sharpen produces a sharpened version of the image. |
| // Sigma parameter must be positive and indicates how much the image will be sharpened. |
| // |
| // Usage example: |
| // |
| // dstImage := imaging.Sharpen(srcImage, 3.5) |
| // |
| func Sharpen(img image.Image, sigma float64) *image.NRGBA { |
| if sigma <= 0 { |
| return Clone(img) |
| } |
| |
| src := newScanner(img) |
| dst := image.NewNRGBA(image.Rect(0, 0, src.w, src.h)) |
| blurred := Blur(img, sigma) |
| |
| parallel(0, src.h, func(ys <-chan int) { |
| scanLine := make([]uint8, src.w*4) |
| for y := range ys { |
| src.scan(0, y, src.w, y+1, scanLine) |
| j := y * dst.Stride |
| for i := 0; i < src.w*4; i++ { |
| val := int(scanLine[i])<<1 - int(blurred.Pix[j]) |
| if val < 0 { |
| val = 0 |
| } else if val > 0xff { |
| val = 0xff |
| } |
| dst.Pix[j] = uint8(val) |
| j++ |
| } |
| } |
| }) |
| |
| return dst |
| } |