Contrast.py 5.6 KB

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