Details.py 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. import cv2
  2. import numpy
  3. import Tool
  4. from PF2.Tools import Contrast
  5. class Details(Tool.Tool):
  6. def on_init(self):
  7. self.id = "details"
  8. self.name = "Details and Edges"
  9. self.icon_path = "ui/PF2_Icons/Details.png"
  10. self.properties = [
  11. # Detailer
  12. Tool.Property("d_enabled", "Detailer", "Header", False, has_toggle=True, has_button=False),
  13. Tool.Property("d_strength", "Strength", "Slider", 30, max=100, min=0),
  14. Tool.Property("d_detail", "Detail", "Slider", 15, max=100, min=0),
  15. Tool.Property("d_sint", "Highlight Intensity", "Slider", 0, max=100, min=-100),
  16. Tool.Property("d_mint", "Midtone Intensity", "Slider", 0, max=100, min=-100),
  17. Tool.Property("d_hint", "Shadow Intensity", "Slider", 0, max=100, min=-100),
  18. Tool.Property("d_slum", "Highlight Luminosity", "Slider", 0, max=100, min=-100),
  19. Tool.Property("d_mlum", "Midtone Luminosity", "Slider", 0, max=100, min=-100),
  20. Tool.Property("d_hlum", "Shadow Luminosity", "Slider", 0, max=100, min=-100),
  21. Tool.Property("d_pcont", "Restorative Contrast", "Slider", 0, max=100, min=0),
  22. # Edges
  23. Tool.Property("e_enabled", "Edges", "Header", False, has_toggle=True, has_button=False),
  24. Tool.Property("e_strength", "Strength", "Slider", 30, max=100, min=0),
  25. Tool.Property("e_fthresh", "First Threshold", "Slider", 100, max=1000, min=0),
  26. Tool.Property("e_sthresh", "Second Threshold", "Slider", 200, max=1000, min=0),
  27. ]
  28. self.contrast_tool = Contrast.Contrast()
  29. self.contrast_tool_restore = Contrast.Contrast()
  30. def on_update(self, image):
  31. im = image
  32. if(self.props["d_enabled"].get_value()):
  33. strength = self.props["d_strength"].get_value()
  34. detail = self.props["d_detail"].get_value()
  35. self.contrast_tool.props["highlight_contrast"].set_value(self.props["d_hint"].get_value())
  36. self.contrast_tool.props["midtone_contrast"].set_value(self.props["d_mint"].get_value())
  37. self.contrast_tool.props["shadow_contrast"].set_value(self.props["d_sint"].get_value())
  38. self.contrast_tool.props["highlight_brightness"].set_value(self.props["d_hlum"].get_value())
  39. self.contrast_tool.props["midtone_brightness"].set_value(self.props["d_mlum"].get_value())
  40. self.contrast_tool.props["shadow_brightness"].set_value(self.props["d_slum"].get_value())
  41. pcont = self.props["d_pcont"].get_value()
  42. # Convert to Grayscale
  43. gray = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY)
  44. # Invert
  45. edged = gray.max() - gray
  46. # Apply Brightness and Contrast
  47. edged = self.contrast_tool.on_update(edged)
  48. edged = cv2.UMat(edged)
  49. # Blur
  50. if(detail > 0):
  51. height, width = image.shape[:2]
  52. size = (height * width)
  53. mul = numpy.math.sqrt(size) / 1064.416
  54. blur_size = abs(2 * round((round(detail*mul) + 1) / 2) - 1)
  55. blurred = cv2.GaussianBlur(edged, (int(blur_size), int(blur_size)), 0)
  56. else:
  57. blurred = edged
  58. # Overlay
  59. colour = cv2.cvtColor(blurred, cv2.COLOR_GRAY2BGR)
  60. bpp = int(str(im.dtype).replace("uint", "").replace("float", ""))
  61. blended = self._overlay(colour.get(), im, float((2 ** bpp) - 1), im.dtype)
  62. # Restore Contrast
  63. self.contrast_tool_restore.props["highlight_contrast"].set_value(pcont * 0.25)
  64. self.contrast_tool_restore.props["midtone_contrast"].set_value(pcont * 0.5)
  65. self.contrast_tool_restore.props["shadow_contrast"].set_value(pcont * 0.25)
  66. cfixed = self.contrast_tool_restore.on_update(blended)
  67. # Blend
  68. if(strength != 100):
  69. im = cv2.addWeighted(cfixed, (strength/100), image, 1 - (strength/100), 0).astype(image.dtype)
  70. else:
  71. im = cfixed.astype(image.dtype)
  72. if(self.props["e_enabled"].get_value()):
  73. strength = self.props["e_strength"].get_value()
  74. t1 = self.props["e_fthresh"].get_value()
  75. t2 = self.props["e_sthresh"].get_value()
  76. # Get bits per pixel
  77. bpp = int(str(im.dtype).replace("uint", "").replace("float", ""))
  78. # Convert to 8 Bit
  79. eight = ((im / float(2 ** bpp)) * 255).astype(numpy.uint8)
  80. # Make Grayscale
  81. grey = cv2.cvtColor(eight, cv2.COLOR_BGR2GRAY)
  82. # Find edges
  83. edged = cv2.Canny(grey, t1, t2)
  84. # Convert edges to colour
  85. colour = cv2.cvtColor(edged, cv2.COLOR_GRAY2BGR)
  86. colour[colour != 0] = 255
  87. colour[colour == 0] = 128
  88. # Convert edges to current bpp
  89. nbpp = (colour / 255.0) * (2 ** bpp)
  90. # Blur?
  91. blurred = cv2.GaussianBlur(nbpp, (7, 7), 0)
  92. # Uhh
  93. blurred[blurred == (2 ** bpp) - 1] = ((2 ** bpp) - 1) / 2.0
  94. # I'm going to be honest, I just copied this one from
  95. # PhotoFiddle 1, I don't know what I was doing
  96. overlayed = self._overlay(blurred, im, float((2 ** bpp) - 1), im.dtype)
  97. out = cv2.addWeighted(overlayed, (strength / 100), im, 1 - (strength / 100), 0)
  98. im = out
  99. return im
  100. def _overlay(self, B, A, bpp, utype):
  101. a = A / bpp
  102. b = B / bpp
  103. merged = (1 - 2 * b) * a ** 2 + 2 * a * b
  104. return (merged * bpp).astype(utype)