Contrast.py 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. import numpy
  2. import Tool
  3. class Contrast(Tool.Tool):
  4. def on_init(self):
  5. self.id = "contrast"
  6. self.name = "Brightness and Contrast"
  7. self.icon_path = "ui/PF2_Icons/Contrast.png"
  8. self.properties = [
  9. Tool.Property("header", "Brightness and Contrast", "Header", None, has_toggle=False, has_button=False),
  10. Tool.Property("overall_brightness", "Brightness", "Slider", 0, max=50, min=-50),
  11. Tool.Property("overall_contrast", "Contrast", "Slider", 0, max=50, min=-50),
  12. Tool.Property("hist_stretch", "Histogram Strecthing", "Header", None, has_toggle=False, has_button=False),
  13. Tool.Property("left_stretch", "Left Stretch", "Slider", 0, max=50, min=-50),
  14. Tool.Property("right_stretch", "Right Stretch", "Slider", 0, max=50, min=-50),
  15. Tool.Property("tonal_header", "Tonal Brightness and Contrast", "Header", None, has_toggle=False, has_button=False),
  16. Tool.Property("highlight_brightness", "Highlight Brightness", "Slider", 0, max=50, min=-50),
  17. Tool.Property("highlight_contrast", "Highlight Contrast", "Slider", 0, max=50, min=-50),
  18. Tool.Property("midtone_brightness", "Midtone Brightness", "Slider", 0, max=50, min=-50),
  19. Tool.Property("midtone_contrast", "Midtone Contrast", "Slider", 0, max=50, min=-50),
  20. Tool.Property("shadow_brightness", "Shadow Brightness", "Slider", 0, max=50, min=-50),
  21. Tool.Property("shadow_contrast", "Shadow Contrast", "Slider", 0, max=50, min=-50),
  22. Tool.Property("tonal_header", "Tonal Bleed Fraction", "Header", None, has_toggle=False,
  23. has_button=False),
  24. Tool.Property("highlight_bleed", "Highlight Bleed", "Slider", 0.5, max=1, min=0.001),
  25. Tool.Property("midtone_bleed", "Midtone Bleed", "Slider", 0.5, max=1, min=0.001),
  26. Tool.Property("shadow_bleed", "Shadow Bleed", "Slider", 0.5, max=1, min=0.001),
  27. ]
  28. def on_update(self, im):
  29. # Apply Contrast Stuff
  30. if(not self.is_default()):
  31. ob = self.props["overall_brightness"].get_value()
  32. oc = -self.props["overall_contrast"].get_value()
  33. ls = -self.props["left_stretch"].get_value()
  34. rs = -self.props["right_stretch"].get_value()
  35. hb = self.props["highlight_brightness"].get_value()
  36. hc = self.props["highlight_contrast"].get_value()
  37. mb = self.props["midtone_brightness"].get_value()
  38. mc = self.props["midtone_contrast"].get_value()
  39. sb = self.props["shadow_brightness"].get_value()
  40. sc = self.props["shadow_contrast"].get_value()
  41. hbl = self.props["highlight_bleed"].get_value()
  42. mbl = self.props["midtone_bleed"].get_value()
  43. sbl = self.props["shadow_bleed"].get_value()
  44. # Add overall brightness
  45. hb += ob
  46. mb += ob
  47. sb += ob
  48. # Add overall contrast
  49. hc -= oc
  50. mc -= oc
  51. sc -= oc
  52. # Bits per pixel
  53. bpp = float(str(im.dtype).replace("uint", "").replace("float", ""))
  54. # Pixel value range
  55. np = float(2 ** bpp - 1)
  56. out = im.astype(numpy.float32)
  57. # Histogram Stretch
  58. if(rs != 0 or ls != 0):
  59. maxpxv = np + (rs/100.0)*np
  60. minpxv = 0 - (ls/100.0)*np
  61. out = (out - minpxv) * (np/float(maxpxv))
  62. # Highlights
  63. isHr = self._is_highlight(out, (3.0 / hbl))
  64. if (hc != 0.0):
  65. # Highlight Contrast
  66. hn = np + 4
  67. hc = (hc / 100.0) * np + 0.8
  68. out = (((hn * ((hc * isHr) + np)) / (np * (hn - (hc * isHr)))) * (out - np / 2.0) + np / 2.0)
  69. if (hb != 0.0):
  70. # Highlight Brightness
  71. out = (out + ((hb * isHr) / 100.0) * np)
  72. # Midtones
  73. isMr = self._is_midtone(out, (3.0 / mbl))
  74. if (mc != 0.0):
  75. # Midtone Contrast
  76. hn = np + 4
  77. mc = (mc / 100.0) * np + 0.8
  78. out = (((hn * ((mc * isMr) + np)) / (np * (hn - (mc * isMr)))) * (out - np / 2.0) + np / 2.0)
  79. if (mb != 0.0):
  80. # Midtone Brightness
  81. out = (out + ((mb * isMr) / 100.0) * np)
  82. # Shadows
  83. isSr = self._is_shadow(out, (3.0 / sbl))
  84. if (sc != 0.0):
  85. # Shadow Contrast
  86. hn = np + 4
  87. sc = (sc / 100.0) * np + 0.8
  88. out = (((hn * ((sc * isSr) + np)) / (np * (hn - (sc * isSr)))) * (out - np / 2.0) + np / 2.0)
  89. if (sb != 0.0):
  90. # Shadow Brightness
  91. out = (out + ((sb * isSr) / 100.0) * np)
  92. # Clip any values out of bounds
  93. out[out < 0.0] = 0.0
  94. out[out > np] = np
  95. return out.astype(im.dtype)
  96. else:
  97. return im
  98. def _is_highlight(self, image, bleed_value = 6.0):
  99. bleed = float(image.max() / bleed_value)
  100. mif = image.max() / 3.0 * 2.0
  101. icopy = image.copy()
  102. icopy[icopy < mif - bleed] = 0.0
  103. icopy[(icopy < mif) * (icopy != 0.0)] = ((mif - (icopy[(icopy < mif) * (icopy != 0.0)])) / bleed) * -1 + 1
  104. icopy[icopy >= mif] = 1.0
  105. return icopy
  106. def _is_midtone(self, image, bleed_value = 6.0):
  107. bleed = float(image.max() / bleed_value)
  108. mif = image.max() / 3.0
  109. mir = image.max() / 3.0 * 2.0
  110. icopy = image.copy()
  111. icopy[icopy < mif - bleed] = 0.0
  112. icopy[icopy > mir + bleed] = 0.0
  113. icopy[(icopy < mif) * (icopy != 0.0)] = ((mif - (icopy[(icopy < mif) * (icopy != 0.0)])) / bleed) * -1 + 1
  114. icopy[(icopy > mir) * (icopy != 0.0)] = (((icopy[(icopy > mir) * (icopy != 0.0)]) - mir) / bleed) * -1 + 1
  115. icopy[(icopy >= mif) * (icopy <= mir)] = 1.0
  116. return icopy
  117. def _is_shadow(self, image, bleed_value=6.0):
  118. bleed = float(image.max() / bleed_value)
  119. mir = image.max() / 3.0
  120. icopy = image.copy()
  121. icopy[icopy <= mir] = 1.0
  122. icopy[icopy > mir + bleed] = 0.0
  123. icopy[icopy > mir] = (((icopy[(icopy > mir) * (icopy != 0.0)]) - mir) / bleed) * -1 + 1
  124. return icopy