blur.go 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. package imaging
  2. import (
  3. "errors"
  4. "image"
  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 absint(i int) int {
  11. if i < 0 {
  12. return -i
  13. }
  14. return i
  15. }
  16. // Blur produces a blurred version of the image using a Gaussian function.
  17. // Sigma parameter must be positive and indicates how much the image will be blurred.
  18. //
  19. // Usage example:
  20. //
  21. // dstImage := imaging.Blur(srcImage, 3.5)
  22. //
  23. func Blur(img image.Image, sigma float64) *image.NRGBA {
  24. if sigma <= 0 {
  25. errors.New("sigma parameter must be positive")
  26. }
  27. src := toNRGBA(img)
  28. radius := int(math.Ceil(sigma * 2.0))
  29. kernel := make([]float64, radius+1)
  30. for i := 0; i <= radius; i++ {
  31. kernel[i] = gaussianBlurKernel(float64(i), sigma)
  32. }
  33. var dst *image.NRGBA
  34. dst = blurHorizontal(src, kernel)
  35. dst = blurVertical(dst, kernel)
  36. return dst
  37. }
  38. func blurHorizontal(src *image.NRGBA, kernel []float64) *image.NRGBA {
  39. radius := len(kernel) - 1
  40. width := src.Bounds().Max.X
  41. height := src.Bounds().Max.Y
  42. dst := image.NewNRGBA(image.Rect(0, 0, width, height))
  43. parallel(width, func(partStart, partEnd int) {
  44. for x := partStart; x < partEnd; x++ {
  45. start := x - radius
  46. if start < 0 {
  47. start = 0
  48. }
  49. end := x + radius
  50. if end > width-1 {
  51. end = width - 1
  52. }
  53. weightSum := 0.0
  54. for ix := start; ix <= end; ix++ {
  55. weightSum += kernel[absint(x-ix)]
  56. }
  57. for y := 0; y < height; y++ {
  58. r, g, b, a := 0.0, 0.0, 0.0, 0.0
  59. for ix := start; ix <= end; ix++ {
  60. weight := kernel[absint(x-ix)]
  61. i := y*src.Stride + ix*4
  62. r += float64(src.Pix[i+0]) * weight
  63. g += float64(src.Pix[i+1]) * weight
  64. b += float64(src.Pix[i+2]) * weight
  65. a += float64(src.Pix[i+3]) * weight
  66. }
  67. r = math.Min(math.Max(r/weightSum, 0.0), 255.0)
  68. g = math.Min(math.Max(g/weightSum, 0.0), 255.0)
  69. b = math.Min(math.Max(b/weightSum, 0.0), 255.0)
  70. a = math.Min(math.Max(a/weightSum, 0.0), 255.0)
  71. j := y*dst.Stride + x*4
  72. dst.Pix[j+0] = uint8(r + 0.5)
  73. dst.Pix[j+1] = uint8(g + 0.5)
  74. dst.Pix[j+2] = uint8(b + 0.5)
  75. dst.Pix[j+3] = uint8(a + 0.5)
  76. }
  77. }
  78. })
  79. return dst
  80. }
  81. func blurVertical(src *image.NRGBA, kernel []float64) *image.NRGBA {
  82. radius := len(kernel) - 1
  83. width := src.Bounds().Max.X
  84. height := src.Bounds().Max.Y
  85. dst := image.NewNRGBA(image.Rect(0, 0, width, height))
  86. parallel(height, func(partStart, partEnd int) {
  87. for y := partStart; y < partEnd; y++ {
  88. start := y - radius
  89. if start < 0 {
  90. start = 0
  91. }
  92. end := y + radius
  93. if end > height-1 {
  94. end = height - 1
  95. }
  96. weightSum := 0.0
  97. for iy := start; iy <= end; iy++ {
  98. weightSum += kernel[absint(y-iy)]
  99. }
  100. for x := 0; x < width; x++ {
  101. r, g, b, a := 0.0, 0.0, 0.0, 0.0
  102. for iy := start; iy <= end; iy++ {
  103. weight := kernel[absint(y-iy)]
  104. i := iy*src.Stride + x*4
  105. r += float64(src.Pix[i+0]) * weight
  106. g += float64(src.Pix[i+1]) * weight
  107. b += float64(src.Pix[i+2]) * weight
  108. a += float64(src.Pix[i+3]) * weight
  109. }
  110. r = math.Min(math.Max(r/weightSum, 0.0), 255.0)
  111. g = math.Min(math.Max(g/weightSum, 0.0), 255.0)
  112. b = math.Min(math.Max(b/weightSum, 0.0), 255.0)
  113. a = math.Min(math.Max(a/weightSum, 0.0), 255.0)
  114. j := y*dst.Stride + x*4
  115. dst.Pix[j+0] = uint8(r + 0.5)
  116. dst.Pix[j+1] = uint8(g + 0.5)
  117. dst.Pix[j+2] = uint8(b + 0.5)
  118. dst.Pix[j+3] = uint8(a + 0.5)
  119. }
  120. }
  121. })
  122. return dst
  123. }