effects.go 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  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. // sigma parameter must be positive!
  19. return Clone(img)
  20. }
  21. src := toNRGBA(img)
  22. radius := int(math.Ceil(sigma * 3.0))
  23. kernel := make([]float64, radius+1)
  24. for i := 0; i <= radius; i++ {
  25. kernel[i] = gaussianBlurKernel(float64(i), sigma)
  26. }
  27. var dst *image.NRGBA
  28. dst = blurHorizontal(src, kernel)
  29. dst = blurVertical(dst, kernel)
  30. return dst
  31. }
  32. func blurHorizontal(src *image.NRGBA, kernel []float64) *image.NRGBA {
  33. radius := len(kernel) - 1
  34. width := src.Bounds().Max.X
  35. height := src.Bounds().Max.Y
  36. dst := image.NewNRGBA(image.Rect(0, 0, width, height))
  37. parallel(width, func(partStart, partEnd int) {
  38. for x := partStart; x < partEnd; x++ {
  39. start := x - radius
  40. if start < 0 {
  41. start = 0
  42. }
  43. end := x + radius
  44. if end > width-1 {
  45. end = width - 1
  46. }
  47. weightSum := 0.0
  48. for ix := start; ix <= end; ix++ {
  49. weightSum += kernel[absint(x-ix)]
  50. }
  51. for y := 0; y < height; y++ {
  52. r, g, b, a := 0.0, 0.0, 0.0, 0.0
  53. for ix := start; ix <= end; ix++ {
  54. weight := kernel[absint(x-ix)]
  55. i := y*src.Stride + ix*4
  56. r += float64(src.Pix[i+0]) * weight
  57. g += float64(src.Pix[i+1]) * weight
  58. b += float64(src.Pix[i+2]) * weight
  59. a += float64(src.Pix[i+3]) * weight
  60. }
  61. r = math.Min(math.Max(r/weightSum, 0.0), 255.0)
  62. g = math.Min(math.Max(g/weightSum, 0.0), 255.0)
  63. b = math.Min(math.Max(b/weightSum, 0.0), 255.0)
  64. a = math.Min(math.Max(a/weightSum, 0.0), 255.0)
  65. j := y*dst.Stride + x*4
  66. dst.Pix[j+0] = uint8(r + 0.5)
  67. dst.Pix[j+1] = uint8(g + 0.5)
  68. dst.Pix[j+2] = uint8(b + 0.5)
  69. dst.Pix[j+3] = uint8(a + 0.5)
  70. }
  71. }
  72. })
  73. return dst
  74. }
  75. func blurVertical(src *image.NRGBA, kernel []float64) *image.NRGBA {
  76. radius := len(kernel) - 1
  77. width := src.Bounds().Max.X
  78. height := src.Bounds().Max.Y
  79. dst := image.NewNRGBA(image.Rect(0, 0, width, height))
  80. parallel(height, func(partStart, partEnd int) {
  81. for y := partStart; y < partEnd; y++ {
  82. start := y - radius
  83. if start < 0 {
  84. start = 0
  85. }
  86. end := y + radius
  87. if end > height-1 {
  88. end = height - 1
  89. }
  90. weightSum := 0.0
  91. for iy := start; iy <= end; iy++ {
  92. weightSum += kernel[absint(y-iy)]
  93. }
  94. for x := 0; x < width; x++ {
  95. r, g, b, a := 0.0, 0.0, 0.0, 0.0
  96. for iy := start; iy <= end; iy++ {
  97. weight := kernel[absint(y-iy)]
  98. i := iy*src.Stride + x*4
  99. r += float64(src.Pix[i+0]) * weight
  100. g += float64(src.Pix[i+1]) * weight
  101. b += float64(src.Pix[i+2]) * weight
  102. a += float64(src.Pix[i+3]) * weight
  103. }
  104. r = math.Min(math.Max(r/weightSum, 0.0), 255.0)
  105. g = math.Min(math.Max(g/weightSum, 0.0), 255.0)
  106. b = math.Min(math.Max(b/weightSum, 0.0), 255.0)
  107. a = math.Min(math.Max(a/weightSum, 0.0), 255.0)
  108. j := y*dst.Stride + x*4
  109. dst.Pix[j+0] = uint8(r + 0.5)
  110. dst.Pix[j+1] = uint8(g + 0.5)
  111. dst.Pix[j+2] = uint8(b + 0.5)
  112. dst.Pix[j+3] = uint8(a + 0.5)
  113. }
  114. }
  115. })
  116. return dst
  117. }
  118. // Sharpen produces a sharpened version of the image.
  119. // Sigma parameter must be positive and indicates how much the image will be sharpened.
  120. //
  121. // Usage example:
  122. //
  123. // dstImage := imaging.Sharpen(srcImage, 3.5)
  124. //
  125. func Sharpen(img image.Image, sigma float64) *image.NRGBA {
  126. if sigma <= 0 {
  127. // sigma parameter must be positive!
  128. return Clone(img)
  129. }
  130. src := toNRGBA(img)
  131. blurred := Blur(img, sigma)
  132. width := src.Bounds().Max.X
  133. height := src.Bounds().Max.Y
  134. dst := image.NewNRGBA(image.Rect(0, 0, width, height))
  135. parallel(height, func(partStart, partEnd int) {
  136. for y := partStart; y < partEnd; y++ {
  137. for x := 0; x < width; x++ {
  138. i := y*src.Stride + x*4
  139. for j := 0; j < 4; j++ {
  140. k := i + j
  141. val := int(src.Pix[k]) + (int(src.Pix[k]) - int(blurred.Pix[k]))
  142. if val < 0 {
  143. val = 0
  144. } else if val > 255 {
  145. val = 255
  146. }
  147. dst.Pix[k] = uint8(val)
  148. }
  149. }
  150. }
  151. })
  152. return dst
  153. }