gaussian.go 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. package drawimg
  2. import (
  3. "image"
  4. "image/color"
  5. "math"
  6. )
  7. func gaussianBlurKernel(x, sigma float64) float64 {
  8. return math.Exp(-(x*x)/(2*sigma*sigma)) / (sigma * math.Sqrt(2*math.Pi))
  9. }
  10. func blurHorizontal(src *image.NRGBA, kernel []float64) *image.NRGBA {
  11. radius := len(kernel) - 1
  12. width := src.Bounds().Max.X
  13. height := src.Bounds().Max.Y
  14. dst := image.NewNRGBA(image.Rect(0, 0, width, height))
  15. parallel(width, func(partStart, partEnd int) {
  16. for x := partStart; x < partEnd; x++ {
  17. start := x - radius
  18. if start < 0 {
  19. start = 0
  20. }
  21. end := x + radius
  22. if end > width-1 {
  23. end = width - 1
  24. }
  25. weightSum := 0.0
  26. for ix := start; ix <= end; ix++ {
  27. weightSum += kernel[absint(x-ix)]
  28. }
  29. for y := 0; y < height; y++ {
  30. var r, g, b, a float64
  31. for ix := start; ix <= end; ix++ {
  32. weight := kernel[absint(x-ix)]
  33. i := y*src.Stride + ix*4
  34. wa := float64(src.Pix[i+3]) * weight
  35. r += float64(src.Pix[i+0]) * wa
  36. g += float64(src.Pix[i+1]) * wa
  37. b += float64(src.Pix[i+2]) * wa
  38. a += wa
  39. }
  40. j := y*dst.Stride + x*4
  41. dst.Pix[j+0] = clamp(r / a)
  42. dst.Pix[j+1] = clamp(g / a)
  43. dst.Pix[j+2] = clamp(b / a)
  44. dst.Pix[j+3] = clamp(a / weightSum)
  45. }
  46. }
  47. })
  48. return dst
  49. }
  50. func blurVertical(src *image.NRGBA, kernel []float64) *image.NRGBA {
  51. radius := len(kernel) - 1
  52. width := src.Bounds().Max.X
  53. height := src.Bounds().Max.Y
  54. dst := image.NewNRGBA(image.Rect(0, 0, width, height))
  55. parallel(height, func(partStart, partEnd int) {
  56. for y := partStart; y < partEnd; y++ {
  57. start := y - radius
  58. if start < 0 {
  59. start = 0
  60. }
  61. end := y + radius
  62. if end > height-1 {
  63. end = height - 1
  64. }
  65. weightSum := 0.0
  66. for iy := start; iy <= end; iy++ {
  67. weightSum += kernel[absint(y-iy)]
  68. }
  69. for x := 0; x < width; x++ {
  70. var r, g, b, a float64
  71. for iy := start; iy <= end; iy++ {
  72. weight := kernel[absint(y-iy)]
  73. i := iy*src.Stride + x*4
  74. wa := float64(src.Pix[i+3]) * weight
  75. r += float64(src.Pix[i+0]) * wa
  76. g += float64(src.Pix[i+1]) * wa
  77. b += float64(src.Pix[i+2]) * wa
  78. a += wa
  79. }
  80. j := y*dst.Stride + x*4
  81. dst.Pix[j+0] = clamp(r / a)
  82. dst.Pix[j+1] = clamp(g / a)
  83. dst.Pix[j+2] = clamp(b / a)
  84. dst.Pix[j+3] = clamp(a / weightSum)
  85. }
  86. }
  87. })
  88. return dst
  89. }
  90. // Sharpen produces a sharpened version of the image.
  91. func Sharpen(img image.Image, radius int, sigma float64) *image.NRGBA {
  92. if sigma <= 0 {
  93. // sigma parameter must be positive!
  94. return Clone(img)
  95. }
  96. src := toNRGBA(img)
  97. blurred := Blur(img, radius, sigma)
  98. width := src.Bounds().Max.X
  99. height := src.Bounds().Max.Y
  100. dst := image.NewNRGBA(image.Rect(0, 0, width, height))
  101. parallel(height, func(partStart, partEnd int) {
  102. for y := partStart; y < partEnd; y++ {
  103. for x := 0; x < width; x++ {
  104. i := y*src.Stride + x*4
  105. for j := 0; j < 4; j++ {
  106. k := i + j
  107. val := int(src.Pix[k])<<1 - int(blurred.Pix[k])
  108. if val < 0 {
  109. val = 0
  110. } else if val > 255 {
  111. val = 255
  112. }
  113. dst.Pix[k] = uint8(val)
  114. }
  115. }
  116. }
  117. })
  118. return dst
  119. }
  120. // Blur produces a blurred version of the image using a Gaussian function.
  121. func Blur(img image.Image, radius int, sigma float64) *image.NRGBA {
  122. var dst *image.NRGBA
  123. if sigma <= 0 {
  124. // sigma parameter must be positive!
  125. return Clone(img)
  126. }
  127. src := toNRGBA(img)
  128. kernel := make([]float64, radius+1)
  129. for i := 0; i <= radius; i++ {
  130. kernel[i] = gaussianBlurKernel(float64(i), sigma)
  131. }
  132. dst = blurHorizontal(src, kernel)
  133. dst = blurVertical(dst, kernel)
  134. return dst
  135. }
  136. // IsTooBright assume that average brightness higher than 100 is too bright.
  137. func IsTooBright(img image.Image) bool {
  138. var (
  139. pixCount, totalBrightness float64
  140. )
  141. pixCount = 0
  142. totalBrightness = 0
  143. AdjustFunc(img, func(c color.NRGBA) color.NRGBA {
  144. brightness := 0.2126*float64(c.R) + 0.7152*float64(c.G) + 0.0722*float64(c.B)
  145. totalBrightness += brightness
  146. pixCount++
  147. return c
  148. })
  149. averBrightness := totalBrightness / pixCount
  150. return averBrightness > 100
  151. }