Details.py 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  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. # Blur
  49. if(detail > 0):
  50. height, width = image.shape[:2]
  51. size = (height * width)
  52. mul = numpy.math.sqrt(size) / 1064.416
  53. blur_size = 2 * round((round(detail*mul) + 1) / 2) - 1
  54. blurred = cv2.GaussianBlur(edged, (int(blur_size), int(blur_size)), 0)
  55. else:
  56. blurred = edged
  57. # Overlay
  58. colour = cv2.cvtColor(blurred, cv2.COLOR_GRAY2BGR)
  59. bpp = int(str(im.dtype).replace("uint", "").replace("float", ""))
  60. blended = self._overlay(colour, im, float((2 ** bpp) - 1), im.dtype)
  61. # Restore Contrast
  62. self.contrast_tool_restore.props["highlight_contrast"].set_value(pcont * 0.25)
  63. self.contrast_tool_restore.props["midtone_contrast"].set_value(pcont * 0.5)
  64. self.contrast_tool_restore.props["shadow_contrast"].set_value(pcont * 0.25)
  65. cfixed = self.contrast_tool_restore.on_update(blended)
  66. # Blend
  67. if(strength != 100):
  68. im = cv2.addWeighted(cfixed, (strength/100), image, 1 - (strength/100), 0).astype(image.dtype)
  69. else:
  70. im = cfixed.astype(image.dtype)
  71. if(self.props["e_enabled"].get_value()):
  72. strength = self.props["e_strength"].get_value()
  73. t1 = self.props["e_fthresh"].get_value()
  74. t2 = self.props["e_sthresh"].get_value()
  75. # Get bits per pixel
  76. bpp = int(str(im.dtype).replace("uint", "").replace("float", ""))
  77. # Convert to 8 Bit
  78. eight = ((im / float(2 ** bpp)) * 255).astype(numpy.uint8)
  79. # Make Grayscale
  80. grey = cv2.cvtColor(eight, cv2.COLOR_BGR2GRAY)
  81. # Find edges
  82. edged = cv2.Canny(grey, t1, t2)
  83. # Convert edges to colour
  84. colour = cv2.cvtColor(edged, cv2.COLOR_GRAY2BGR)
  85. colour[colour != 0] = 255
  86. colour[colour == 0] = 128
  87. # Convert edges to current bpp
  88. nbpp = (colour / 255.0) * (2 ** bpp)
  89. # Blur?
  90. blurred = cv2.GaussianBlur(nbpp, (7, 7), 0)
  91. # Uhh
  92. blurred[blurred == (2 ** bpp) - 1] = ((2 ** bpp) - 1) / 2.0
  93. # I'm going to be honest, I just copied this one from
  94. # PhotoFiddle 1, I don't know what I was doing
  95. overlayed = self._overlay(blurred, im, float((2 ** bpp) - 1), im.dtype)
  96. out = cv2.addWeighted(overlayed, (strength / 100), im, 1 - (strength / 100), 0)
  97. im = out
  98. return im
  99. def _overlay(self, B, A, bpp, utype):
  100. a = A / bpp
  101. b = B / bpp
  102. merged = (1 - 2 * b) * a ** 2 + 2 * a * b
  103. return (merged * bpp).astype(utype)