effects.go 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  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. // Usage 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. for y := range ys {
  34. src.scan(0, y, src.w, y+1, scanLine)
  35. for x := 0; x < src.w; x++ {
  36. min := x - radius
  37. if min < 0 {
  38. min = 0
  39. }
  40. max := x + radius
  41. if max > src.w-1 {
  42. max = src.w - 1
  43. }
  44. var r, g, b, a, wsum float64
  45. for ix := min; ix <= max; ix++ {
  46. i := ix * 4
  47. weight := kernel[absint(x-ix)]
  48. wsum += weight
  49. wa := float64(scanLine[i+3]) * weight
  50. r += float64(scanLine[i+0]) * wa
  51. g += float64(scanLine[i+1]) * wa
  52. b += float64(scanLine[i+2]) * wa
  53. a += wa
  54. }
  55. if a != 0 {
  56. r /= a
  57. g /= a
  58. b /= a
  59. }
  60. j := y*dst.Stride + x*4
  61. dst.Pix[j+0] = clamp(r)
  62. dst.Pix[j+1] = clamp(g)
  63. dst.Pix[j+2] = clamp(b)
  64. dst.Pix[j+3] = clamp(a / wsum)
  65. }
  66. }
  67. })
  68. return dst
  69. }
  70. func blurVertical(img image.Image, kernel []float64) *image.NRGBA {
  71. src := newScanner(img)
  72. dst := image.NewNRGBA(image.Rect(0, 0, src.w, src.h))
  73. radius := len(kernel) - 1
  74. parallel(0, src.w, func(xs <-chan int) {
  75. scanLine := make([]uint8, src.h*4)
  76. for x := range xs {
  77. src.scan(x, 0, x+1, src.h, scanLine)
  78. for y := 0; y < src.h; y++ {
  79. min := y - radius
  80. if min < 0 {
  81. min = 0
  82. }
  83. max := y + radius
  84. if max > src.h-1 {
  85. max = src.h - 1
  86. }
  87. var r, g, b, a, wsum float64
  88. for iy := min; iy <= max; iy++ {
  89. i := iy * 4
  90. weight := kernel[absint(y-iy)]
  91. wsum += weight
  92. wa := float64(scanLine[i+3]) * weight
  93. r += float64(scanLine[i+0]) * wa
  94. g += float64(scanLine[i+1]) * wa
  95. b += float64(scanLine[i+2]) * wa
  96. a += wa
  97. }
  98. if a != 0 {
  99. r /= a
  100. g /= a
  101. b /= a
  102. }
  103. j := y*dst.Stride + x*4
  104. dst.Pix[j+0] = clamp(r)
  105. dst.Pix[j+1] = clamp(g)
  106. dst.Pix[j+2] = clamp(b)
  107. dst.Pix[j+3] = clamp(a / wsum)
  108. }
  109. }
  110. })
  111. return dst
  112. }
  113. // Sharpen produces a sharpened version of the image.
  114. // Sigma parameter must be positive and indicates how much the image will be sharpened.
  115. //
  116. // Usage example:
  117. //
  118. // dstImage := imaging.Sharpen(srcImage, 3.5)
  119. //
  120. func Sharpen(img image.Image, sigma float64) *image.NRGBA {
  121. if sigma <= 0 {
  122. return Clone(img)
  123. }
  124. src := newScanner(img)
  125. dst := image.NewNRGBA(image.Rect(0, 0, src.w, src.h))
  126. blurred := Blur(img, sigma)
  127. parallel(0, src.h, func(ys <-chan int) {
  128. scanLine := make([]uint8, src.w*4)
  129. for y := range ys {
  130. src.scan(0, y, src.w, y+1, scanLine)
  131. j := y * dst.Stride
  132. for i := 0; i < src.w*4; i++ {
  133. val := int(scanLine[i])<<1 - int(blurred.Pix[j])
  134. if val < 0 {
  135. val = 0
  136. } else if val > 0xff {
  137. val = 0xff
  138. }
  139. dst.Pix[j] = uint8(val)
  140. j++
  141. }
  142. }
  143. })
  144. return dst
  145. }