159 lines
3.9 KiB
Go
159 lines
3.9 KiB
Go
|
package drawimg
|
||
|
|
||
|
import (
|
||
|
"image"
|
||
|
"image/color"
|
||
|
"math"
|
||
|
)
|
||
|
|
||
|
func gaussianBlurKernel(x, sigma float64) float64 {
|
||
|
return math.Exp(-(x*x)/(2*sigma*sigma)) / (sigma * math.Sqrt(2*math.Pi))
|
||
|
}
|
||
|
|
||
|
func blurHorizontal(src *image.NRGBA, kernel []float64) *image.NRGBA {
|
||
|
radius := len(kernel) - 1
|
||
|
width := src.Bounds().Max.X
|
||
|
height := src.Bounds().Max.Y
|
||
|
dst := image.NewNRGBA(image.Rect(0, 0, width, height))
|
||
|
parallel(width, func(partStart, partEnd int) {
|
||
|
for x := partStart; x < partEnd; x++ {
|
||
|
start := x - radius
|
||
|
if start < 0 {
|
||
|
start = 0
|
||
|
}
|
||
|
end := x + radius
|
||
|
if end > width-1 {
|
||
|
end = width - 1
|
||
|
}
|
||
|
weightSum := 0.0
|
||
|
for ix := start; ix <= end; ix++ {
|
||
|
weightSum += kernel[absint(x-ix)]
|
||
|
}
|
||
|
for y := 0; y < height; y++ {
|
||
|
var r, g, b, a float64
|
||
|
for ix := start; ix <= end; ix++ {
|
||
|
weight := kernel[absint(x-ix)]
|
||
|
i := y*src.Stride + ix*4
|
||
|
wa := float64(src.Pix[i+3]) * weight
|
||
|
r += float64(src.Pix[i+0]) * wa
|
||
|
g += float64(src.Pix[i+1]) * wa
|
||
|
b += float64(src.Pix[i+2]) * wa
|
||
|
a += wa
|
||
|
}
|
||
|
j := y*dst.Stride + x*4
|
||
|
dst.Pix[j+0] = clamp(r / a)
|
||
|
dst.Pix[j+1] = clamp(g / a)
|
||
|
dst.Pix[j+2] = clamp(b / a)
|
||
|
dst.Pix[j+3] = clamp(a / weightSum)
|
||
|
}
|
||
|
}
|
||
|
})
|
||
|
return dst
|
||
|
}
|
||
|
|
||
|
func blurVertical(src *image.NRGBA, kernel []float64) *image.NRGBA {
|
||
|
radius := len(kernel) - 1
|
||
|
width := src.Bounds().Max.X
|
||
|
height := src.Bounds().Max.Y
|
||
|
dst := image.NewNRGBA(image.Rect(0, 0, width, height))
|
||
|
parallel(height, func(partStart, partEnd int) {
|
||
|
for y := partStart; y < partEnd; y++ {
|
||
|
start := y - radius
|
||
|
if start < 0 {
|
||
|
start = 0
|
||
|
}
|
||
|
end := y + radius
|
||
|
if end > height-1 {
|
||
|
end = height - 1
|
||
|
}
|
||
|
weightSum := 0.0
|
||
|
for iy := start; iy <= end; iy++ {
|
||
|
weightSum += kernel[absint(y-iy)]
|
||
|
}
|
||
|
for x := 0; x < width; x++ {
|
||
|
var r, g, b, a float64
|
||
|
for iy := start; iy <= end; iy++ {
|
||
|
weight := kernel[absint(y-iy)]
|
||
|
i := iy*src.Stride + x*4
|
||
|
wa := float64(src.Pix[i+3]) * weight
|
||
|
r += float64(src.Pix[i+0]) * wa
|
||
|
g += float64(src.Pix[i+1]) * wa
|
||
|
b += float64(src.Pix[i+2]) * wa
|
||
|
a += wa
|
||
|
}
|
||
|
j := y*dst.Stride + x*4
|
||
|
dst.Pix[j+0] = clamp(r / a)
|
||
|
dst.Pix[j+1] = clamp(g / a)
|
||
|
dst.Pix[j+2] = clamp(b / a)
|
||
|
dst.Pix[j+3] = clamp(a / weightSum)
|
||
|
}
|
||
|
}
|
||
|
})
|
||
|
return dst
|
||
|
}
|
||
|
|
||
|
// Sharpen produces a sharpened version of the image.
|
||
|
func Sharpen(img image.Image, radius int, sigma float64) *image.NRGBA {
|
||
|
if sigma <= 0 {
|
||
|
// sigma parameter must be positive!
|
||
|
return Clone(img)
|
||
|
}
|
||
|
src := toNRGBA(img)
|
||
|
blurred := Blur(img, radius, sigma)
|
||
|
width := src.Bounds().Max.X
|
||
|
height := src.Bounds().Max.Y
|
||
|
dst := image.NewNRGBA(image.Rect(0, 0, width, height))
|
||
|
parallel(height, func(partStart, partEnd int) {
|
||
|
for y := partStart; y < partEnd; y++ {
|
||
|
for x := 0; x < width; x++ {
|
||
|
i := y*src.Stride + x*4
|
||
|
for j := 0; j < 4; j++ {
|
||
|
k := i + j
|
||
|
val := int(src.Pix[k])<<1 - int(blurred.Pix[k])
|
||
|
if val < 0 {
|
||
|
val = 0
|
||
|
} else if val > 255 {
|
||
|
val = 255
|
||
|
}
|
||
|
dst.Pix[k] = uint8(val)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
})
|
||
|
return dst
|
||
|
}
|
||
|
|
||
|
// Blur produces a blurred version of the image using a Gaussian function.
|
||
|
func Blur(img image.Image, radius int, sigma float64) *image.NRGBA {
|
||
|
var dst *image.NRGBA
|
||
|
if sigma <= 0 {
|
||
|
// sigma parameter must be positive!
|
||
|
return Clone(img)
|
||
|
}
|
||
|
src := toNRGBA(img)
|
||
|
kernel := make([]float64, radius+1)
|
||
|
for i := 0; i <= radius; i++ {
|
||
|
kernel[i] = gaussianBlurKernel(float64(i), sigma)
|
||
|
}
|
||
|
dst = blurHorizontal(src, kernel)
|
||
|
dst = blurVertical(dst, kernel)
|
||
|
return dst
|
||
|
}
|
||
|
|
||
|
// IsTooBright assume that average brightness higher than 100 is too bright.
|
||
|
func IsTooBright(img image.Image) bool {
|
||
|
var (
|
||
|
pixCount, totalBrightness float64
|
||
|
)
|
||
|
pixCount = 0
|
||
|
totalBrightness = 0
|
||
|
AdjustFunc(img, func(c color.NRGBA) color.NRGBA {
|
||
|
brightness := 0.2126*float64(c.R) + 0.7152*float64(c.G) + 0.0722*float64(c.B)
|
||
|
totalBrightness += brightness
|
||
|
pixCount++
|
||
|
return c
|
||
|
})
|
||
|
averBrightness := totalBrightness / pixCount
|
||
|
return averBrightness > 100
|
||
|
}
|