1
0

adjust.go 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. package imaging
  2. import (
  3. "image"
  4. "image/color"
  5. "math"
  6. )
  7. func applyColorMapping(img image.Image, fn func(c color.NRGBA) color.NRGBA) *image.NRGBA {
  8. src := toNRGBA(img)
  9. width := src.Bounds().Max.X
  10. height := src.Bounds().Max.Y
  11. dst := image.NewNRGBA(image.Rect(0, 0, width, height))
  12. parallel(height, func(partStart, partEnd int) {
  13. for y := partStart; y < partEnd; y++ {
  14. for x := 0; x < width; x++ {
  15. i := y*src.Stride + x*4
  16. j := y*dst.Stride + x*4
  17. r := src.Pix[i+0]
  18. g := src.Pix[i+1]
  19. b := src.Pix[i+2]
  20. a := src.Pix[i+3]
  21. c := fn(color.NRGBA{r, g, b, a})
  22. dst.Pix[j+0] = c.R
  23. dst.Pix[j+1] = c.G
  24. dst.Pix[j+2] = c.B
  25. dst.Pix[j+3] = c.A
  26. }
  27. }
  28. })
  29. return dst
  30. }
  31. // clamp & round float64 to uint8 (0..255)
  32. func clamp(v float64) uint8 {
  33. return uint8(math.Min(math.Max(v, 0.0), 255.0) + 0.5)
  34. }
  35. // AdjustGamma performs a gamma correction on the image and returns the adjusted image.
  36. // Gamma parameter must be positive. Gamma = 1.0 gives the original image.
  37. // Gamma less than 1.0 darkens the image and gamma greater than 1.0 lightens it.
  38. //
  39. // Example:
  40. //
  41. // dstImage = imaging.AdjustGamma(srcImage, 0.7)
  42. //
  43. func AdjustGamma(img image.Image, gamma float64) *image.NRGBA {
  44. e := 1.0 / math.Max(gamma, 0.0001)
  45. lut := make([]uint8, 256)
  46. for i := 0; i < 256; i++ {
  47. lut[i] = clamp(math.Pow(float64(i)/255.0, e) * 255.0)
  48. }
  49. fn := func(c color.NRGBA) color.NRGBA {
  50. return color.NRGBA{lut[c.R], lut[c.G], lut[c.B], c.A}
  51. }
  52. return applyColorMapping(img, fn)
  53. }
  54. // AdjustContrast changes the contrast of the image using the percentage parameter and returns the adjusted image.
  55. // The percentage must be in range (-100, 100). The percentage = 0 gives the original image.
  56. // The percentage = -100 gives solid grey image.
  57. //
  58. // Examples:
  59. //
  60. // dstImage = imaging.AdjustContrast(srcImage, -10) // decrease image contrast by 10%
  61. // dstImage = imaging.AdjustContrast(srcImage, 20) // increase image contrast by 20%
  62. //
  63. func AdjustContrast(img image.Image, percentage float64) *image.NRGBA {
  64. percentage = math.Min(math.Max(percentage, -100.0), 100.0)
  65. lut := make([]uint8, 256)
  66. v := (100.0 + percentage) / 100.0
  67. for i := 0; i < 256; i++ {
  68. if 0 <= v && v <= 1 {
  69. lut[i] = clamp((0.5 + (float64(i)/255.0-0.5)*v) * 255.0)
  70. } else if 1 < v && v < 2 {
  71. lut[i] = clamp((0.5 + (float64(i)/255.0-0.5)*(1/(2.0-v))) * 255.0)
  72. } else {
  73. lut[i] = uint8(float64(i)/255.0+0.5) * 255
  74. }
  75. }
  76. fn := func(c color.NRGBA) color.NRGBA {
  77. return color.NRGBA{lut[c.R], lut[c.G], lut[c.B], c.A}
  78. }
  79. return applyColorMapping(img, fn)
  80. }
  81. // AdjustBrightness changes the brightness of the image using the percentage parameter and returns the adjusted image.
  82. // The percentage must be in range (-100, 100). The percentage = 0 gives the original image.
  83. // The percentage = -100 gives solid black image. The percentage = 100 gives solid white image.
  84. //
  85. // Examples:
  86. //
  87. // dstImage = imaging.AdjustBrightness(srcImage, -15) // decrease image brightness by 15%
  88. // dstImage = imaging.AdjustBrightness(srcImage, 10) // increase image brightness by 10%
  89. //
  90. func AdjustBrightness(img image.Image, percentage float64) *image.NRGBA {
  91. percentage = math.Min(math.Max(percentage, -100.0), 100.0)
  92. lut := make([]uint8, 256)
  93. shift := 255.0 * percentage / 100.0
  94. for i := 0; i < 256; i++ {
  95. lut[i] = clamp(float64(i) + shift)
  96. }
  97. fn := func(c color.NRGBA) color.NRGBA {
  98. return color.NRGBA{lut[c.R], lut[c.G], lut[c.B], c.A}
  99. }
  100. return applyColorMapping(img, fn)
  101. }
  102. // Grayscale produces grayscale version of the image.
  103. func Grayscale(img image.Image) *image.NRGBA {
  104. fn := func(c color.NRGBA) color.NRGBA {
  105. f := 0.299*float64(c.R) + 0.587*float64(c.G) + 0.114*float64(c.B)
  106. y := uint8(f + 0.5)
  107. return color.NRGBA{y, y, y, c.A}
  108. }
  109. return applyColorMapping(img, fn)
  110. }
  111. // Invert produces inverted (negated) version of the image.
  112. func Invert(img image.Image) *image.NRGBA {
  113. fn := func(c color.NRGBA) color.NRGBA {
  114. return color.NRGBA{255 - c.R, 255 - c.G, 255 - c.B, c.A}
  115. }
  116. return applyColorMapping(img, fn)
  117. }