__init__.py 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. import getpass
  2. import glob
  3. import os
  4. import subprocess
  5. import shutil
  6. import threading
  7. import cv2
  8. import Activity
  9. from gi.repository import GLib, Gtk, GdkPixbuf, Pango
  10. import Export
  11. import Activity.Mask.Mask as Mask
  12. import numpy
  13. import Activity.Mask.Inpaint as Inpaint
  14. class Inpainter(Activity.Activity):
  15. def on_init(self):
  16. UI_FILE = "ui/Mask_Activity.glade"
  17. self.builder.add_from_file(UI_FILE)
  18. print(UI_FILE)
  19. self.widget = self.builder.get_object("Mask_main")
  20. self.stack.add_titled(self.widget, self.id, self.name)
  21. self.header_widget = self.builder.get_object("Mask_header")
  22. self.header_stack.add_titled(self.header_widget, self.id, self.name)
  23. # Get all UI Components
  24. self.ui = {}
  25. components = [
  26. "open_window",
  27. "open_chooser",
  28. "preview_button",
  29. "open_header",
  30. "open_open_button",
  31. "open_cancel_button",
  32. "open",
  33. "popovermenu",
  34. "toolbar",
  35. "scroll_window",
  36. "preview",
  37. "mask_draw_toggle",
  38. "mask_erase_toggle",
  39. "preview_eventbox",
  40. "scroll_window",
  41. "mask_brush_size",
  42. "preview_button"
  43. ]
  44. self.paths = []
  45. self.exporting = False
  46. for component in components:
  47. self.ui[component] = self.builder.get_object("Mask_%s" % component))
  48. self.menu_popover = self.ui["popovermenu"]
  49. self.image_readonly = None
  50. self.image = None
  51. self.image_pb = None
  52. self.mousedown = False
  53. self.mask = None
  54. self.current_path = None
  55. self.actual_width = 0
  56. self.ui["open_window"].set_transient_for(self.root)
  57. self.ui["open_window"].set_titlebar(self.ui["open_header"])
  58. self.ui["open_open_button"].connect("clicked", self.on_file_opened)
  59. self.ui["open_cancel_button"].connect("clicked", self.on_file_canceled)
  60. self.ui["preview_eventbox"].connect('motion-notify-event', self.preview_dragged)
  61. self.ui["preview_eventbox"].connect('button-press-event', self.new_path)
  62. self.ui["scroll_window"].connect_after("draw", self.draw_ui_brush_circle)
  63. self.ui["scroll_window"].connect_after('motion-notify-event', self.mouse_coords_changed)
  64. self.ui["preview_button"].connect("clicked", self.create_preview)
  65. self.ui["open"].connect("clicked", self.on_open_clicked)
  66. self.update_enabled()
  67. def update_enabled(self):
  68. self.ui["toolbar"].set_sensitive(type(self.image) == numpy.ndarray)
  69. self.ui["preview_button"].set_sensitive(type(self.image) == numpy.ndarray)
  70. def on_open_clicked(self, sender):
  71. self.ui["open_window"].show_all()
  72. def on_file_canceled(self, sender):
  73. self.ui["open_window"].hide()
  74. def on_file_opened(self, sender):
  75. self.ui["open_window"].hide()
  76. path = self.ui["open_chooser"].get_filename()
  77. threading.Thread(target=self.load_image, args=(path,)).start()
  78. def load_image(self, path):
  79. self.get_sized_image(path)
  80. self.root.get_titlebar().set_subtitle("%s" % path)
  81. def get_sized_image(self, path):
  82. self.start_work()
  83. w = self.ui["scroll_window"].get_allocated_width() - 12
  84. h = self.ui["scroll_window"].get_allocated_height() - 12
  85. threading.Thread(target=self.get_preview_image, args=(path, w, h)).start()
  86. def get_preview_image(self, path, w, h):
  87. if (not os.path.exists("/tmp/inpainter-%s" % getpass.getuser())):
  88. os.mkdir("/tmp/inpainter-%s" % getpass.getuser())
  89. im = cv2.imread(path, 2 | 1)
  90. height, width = im.shape[:2]
  91. self.actual_width = width
  92. # Get fitting size
  93. ratio = float(w) / width
  94. if (height * ratio > h):
  95. ratio = float(h) / height
  96. nw = width * ratio
  97. nh = height * ratio
  98. im = cv2.resize(im, (int(nw), int(nh)), interpolation=cv2.INTER_AREA)
  99. self.image = im
  100. self.image_readonly = im.copy()
  101. self.mask = Mask.Mask(im, width, height)
  102. self.update_preview(im)
  103. def update_preview(self, image):
  104. cv2.imwrite("/tmp/inpainter-%s/pre-preview.tiff" % getpass.getuser(), image)
  105. pb = GdkPixbuf.Pixbuf.new_from_file("/tmp/inpainter-%s/pre-preview.tiff" % getpass.getuser())
  106. shutil.rmtree("/tmp/inpainter-%s" % getpass.getuser())
  107. self.image_pb = pb
  108. GLib.idle_add(self.set_pixbuf)
  109. def set_pixbuf(self):
  110. self.ui["preview"].set_from_pixbuf(self.image_pb)
  111. self.stop_work()
  112. self.update_enabled()
  113. def preview_dragged(self, widget, event):
  114. x, y = widget.translate_coordinates(self.ui["preview"], event.x, event.y)
  115. draw = self.ui["mask_draw_toggle"].get_active()
  116. erase = self.ui["mask_erase_toggle"].get_active()
  117. if((draw or erase) and self.current_path):
  118. if (x < 0.0):
  119. x = 0
  120. if (y < 0.0):
  121. y = 0
  122. pwidth = self.image_pb.get_width()
  123. pheight = self.image_pb.get_height()
  124. if (x > pwidth):
  125. x = pwidth
  126. if (y > pheight):
  127. y = pheight
  128. print(x, y)
  129. fill = (0, 0, 255)
  130. if(erase):
  131. fill = (255, 0, 0)
  132. self.draw_path(x, y, pheight, pwidth, fill)
  133. self.on_mask_change()
  134. self.mouse_down_coords_changed(widget, event)
  135. return True
  136. def draw_path(self, x, y, pheight, pwidth, fill):
  137. preview = self.current_path.add_point(int(x), int(y), (pheight, pwidth, 3), fill)
  138. # Bits per pixel
  139. bpp = float(str(self.image.dtype).replace("uint", "").replace("float", ""))
  140. # Pixel value range
  141. np = float(2 ** bpp - 1)
  142. self.image[preview == 255] = np
  143. cv2.imwrite("/dev/shm/inpaint-preview-%s-drag.png" % getpass.getuser(), self.image)
  144. temppbuf = GdkPixbuf.Pixbuf.new_from_file("/dev/shm/inpaint-preview-%s-drag.png" % getpass.getuser())
  145. self.ui["preview"].set_from_pixbuf(temppbuf)
  146. def new_path(self, widget, event):
  147. draw = self.ui["mask_draw_toggle"].get_active()
  148. erase = self.ui["mask_erase_toggle"].get_active()
  149. if(draw or erase):
  150. print(self.image_pb.get_width(), self.image_pb.get_height())
  151. width = self.image_pb.get_width()
  152. size = self.ui["mask_brush_size"].get_value()
  153. feather = 0
  154. self.current_path = self.mask.mask.get_new_path(size, feather, float(self.actual_width)/float(width), draw)
  155. def mouse_coords_changed(self, widget, event):
  156. self.mousedown = False
  157. self.mousex, self.mousey = event.x, event.y
  158. widget.queue_draw()
  159. def mouse_down_coords_changed(self, widget, event):
  160. self.mousedown = True
  161. self.mousex, self.mousey = widget.translate_coordinates(self.ui["scroll_window"], event.x, event.y)
  162. widget.queue_draw()
  163. def draw_ui_brush_circle(self, widget, context):
  164. if(type(self.image) == numpy.ndarray):
  165. size = self.ui["mask_brush_size"].get_value()
  166. if(self.mousedown):
  167. context.set_source_rgb(255, 0, 0)
  168. else:
  169. context.set_source_rgb(255, 255, 255)
  170. context.arc(self.mousex, self.mousey, size/2.0, 0.0, 2 * numpy.pi)
  171. context.stroke()
  172. def on_mask_change(self):
  173. threading.Thread(target=self.__on_mask_change).start()
  174. def __on_mask_change(self):
  175. image = self.image_readonly.copy()
  176. image = self.mask.get_mask_preview(image)
  177. self.image = image
  178. self.update_preview(image)