effects.go 3.7 KB

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