__init__.py 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  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. from HDR.Methods import Enfuse
  12. from HDR.Methods import Fattal
  13. from HDR.Methods import Mantuik
  14. class HDR(Activity.Activity):
  15. def on_init(self):
  16. self.id = "HDR"
  17. self.name = "High Dynamic Range Stack"
  18. self.subtitle = "Stack Many Images at Different Exposures"
  19. UI_FILE = "ui/HDR_Activity.glade"
  20. self.builder.add_from_file(UI_FILE)
  21. self.widget = self.builder.get_object("HDR_main")
  22. self.stack.add_titled(self.widget, self.id, self.name)
  23. self.header_widget = self.builder.get_object("HDR_header")
  24. self.header_stack.add_titled(self.header_widget, self.id, self.name)
  25. # Get all UI Components
  26. self.ui = {}
  27. components = [
  28. "open_window",
  29. "open_chooser",
  30. "preview_button",
  31. "open_header",
  32. "open_open_button",
  33. "open_cancel_button",
  34. "images",
  35. "preview",
  36. "export_image",
  37. "open_pf2",
  38. "add_button",
  39. "remove_button",
  40. "scroll_window",
  41. "popovermenu",
  42. "stack",
  43. "method"
  44. ]
  45. self.paths = []
  46. self.exporting = False
  47. self.settings = {}
  48. for component in components:
  49. self.ui[component] = self.builder.get_object("%s_%s" % (self.id, component))
  50. self.menu_popover = self.ui["popovermenu"]
  51. self.ui["open_window"].set_transient_for(self.root)
  52. self.ui["open_window"].set_titlebar(self.ui["open_header"])
  53. # Connect Siginals
  54. self.ui["add_button"].connect("clicked", self.on_add_clicked)
  55. self.ui["remove_button"].connect("clicked", self.on_remove_clicked)
  56. self.ui["open_open_button"].connect("clicked", self.on_file_opened)
  57. self.ui["open_cancel_button"].connect("clicked", self.on_file_canceled)
  58. self.ui["preview_button"].connect("clicked", self.on_preview_clicked)
  59. self.ui["export_image"].connect("clicked", self.export_clicked)
  60. self.ui["open_pf2"].connect("clicked", self.export_pf2_clicked)
  61. self.ui["method"].connect("changed", self.on_method_changed)
  62. self.method = None
  63. self.methods = [
  64. Enfuse.Enfuse(),
  65. Fattal.Fattal(),
  66. Mantuik.Mantuik06(),
  67. Mantuik.Mantuik08(),
  68. ]
  69. self.methodMap = {}
  70. for method in self.methods:
  71. self.methodMap[method.name] = method
  72. self.ui["method"].append_text(method.name)
  73. self.ui["stack"].add(method.widget)
  74. self.ui["method"].set_active(0)
  75. self.update_enabled()
  76. def on_add_clicked(self, sender):
  77. self.ui["open_window"].show_all()
  78. def on_file_canceled(self, sender):
  79. self.ui["open_window"].hide()
  80. def on_file_opened(self, sender):
  81. self.ui["open_window"].hide()
  82. paths = self.ui["open_chooser"].get_filenames()
  83. self.show_message("Loading Images", "Please wait while PhotoFiddle loads your images…", True, True)
  84. threading.Thread(target=self.add_images, args=(paths,)).start()
  85. def on_method_changed(self, sender):
  86. self.ui["stack"].set_visible_child(self.methodMap[self.ui["method"].get_active_text()].widget)
  87. def update_enabled(self):
  88. enabled = len(self.get_paths()) > 0
  89. self.ui["export_image"].set_sensitive(enabled)
  90. self.ui["open_pf2"].set_sensitive(enabled)
  91. self.ui["preview_button"].set_sensitive(enabled)
  92. def add_images(self, paths):
  93. c = 0
  94. for path in paths:
  95. # Create box
  96. box = Gtk.HBox()
  97. box.set_spacing(6)
  98. im = Gtk.Image()
  99. pb = GdkPixbuf.Pixbuf.new_from_file_at_scale(path, 128, 128, True)
  100. im.set_from_pixbuf(pb)
  101. box.add(im)
  102. label = Gtk.Label()
  103. label.set_ellipsize(Pango.EllipsizeMode.START)
  104. label.set_text(path)
  105. box.add(label)
  106. box.show_all()
  107. c += 1
  108. GLib.idle_add(self.add_image_row, box, c, len(paths))
  109. GLib.idle_add(self.hide_message)
  110. def add_image_row(self, row, count, length):
  111. self.ui["images"].add(row)
  112. self.update_enabled()
  113. self.update_message_progress(count, length)
  114. def on_remove_clicked(self, sender):
  115. items = self.ui["images"].get_selected_rows()
  116. for item in items:
  117. item.destroy()
  118. self.update_enabled()
  119. def on_open(self, path):
  120. self.root.get_titlebar().set_subtitle("High Dynamic Range")
  121. pass
  122. def on_exit(self):
  123. if(not self.exporting):
  124. self.root.get_titlebar().set_subtitle("")
  125. self.on_init()
  126. return True
  127. return False
  128. def on_preview_clicked(self, sender):
  129. self.show_message("Generating Preview…", "PhotoFiddle is preparing to generate a preview", True, True)
  130. paths = self.get_paths()
  131. self.method = self.methodMap[self.ui["method"].get_active_text()]
  132. w = self.ui["scroll_window"].get_allocated_width() - 12
  133. h = self.ui["scroll_window"].get_allocated_height() - 12
  134. threading.Thread(target=self.do_preview, args=(paths, w, h)).start()
  135. def do_stack(self, paths, output, w, h):
  136. if (not os.path.exists("/tmp/hdr-%s" % getpass.getuser())):
  137. os.mkdir("/tmp/hdr-%s" % getpass.getuser())
  138. GLib.idle_add(self.show_message, "HDR Stacking…", "PhotoFiddle is processing images…", True, True)
  139. length = len(paths)*3
  140. count = 0
  141. images = []
  142. height = 0
  143. width = 0
  144. for path in paths:
  145. GLib.idle_add(self.update_message_progress, count, length)
  146. im = cv2.imread(path, 2 | 1)
  147. height, width = im.shape[:2]
  148. # Get fitting size
  149. ratio = float(w) / width
  150. if (height * ratio > h):
  151. ratio = float(h) / height
  152. nw = width * ratio
  153. nh = height * ratio
  154. count += 1
  155. GLib.idle_add(self.update_message_progress, count, length)
  156. im = cv2.resize(im, (int(nw), int(nh)), interpolation=cv2.INTER_AREA)
  157. count += 1
  158. GLib.idle_add(self.update_message_progress, count, length)
  159. cv2.imwrite("/tmp/hdr-%s/IN%i.tiff" % (getpass.getuser(), count), im)
  160. images += ["/tmp/hdr-%s/IN%i.tiff" % (getpass.getuser(), count),]
  161. count += 1
  162. GLib.idle_add(self.show_message, "HDR Stacking…", "PhotoFiddle is aligning images…", True, False)
  163. stacked_list = self.method.stack(images)
  164. GLib.idle_add(self.show_message, "HDR Stacking…", "PhotoFiddle is performing an HDR stack…", True, False)
  165. self.method.run(stacked_list, output, width, height)
  166. GLib.idle_add(self.hide_message)
  167. def get_paths(self):
  168. paths = []
  169. for item in self.ui["images"].get_children():
  170. paths += [item.get_children()[0].get_children()[1].get_text(),]
  171. return paths
  172. def do_preview(self, paths, w, h):
  173. self.do_stack(paths, "/tmp/hdr-preview-%s.tiff" % getpass.getuser(), w, h)
  174. pb = GdkPixbuf.Pixbuf.new_from_file("/tmp/hdr-preview-%s.tiff" % getpass.getuser())
  175. GLib.idle_add(self.update_preview, pb)
  176. def update_preview(self, pb):
  177. self.ui["preview"].set_from_pixbuf(pb)
  178. def get_export_image(self, w, h):
  179. GLib.idle_add(self.export_started)
  180. self.do_stack(self.paths, "/tmp/export-hdr-%s.tiff" % getpass.getuser(), w, h)
  181. GLib.idle_add(self.show_message, "Exporting…", "PhotoFiddle is exporting the HDR photo…", True, False)
  182. im = cv2.imread("/tmp/export-hdr-%s.tiff" % getpass.getuser(), 2 | 1)
  183. return im
  184. def export_started(self):
  185. self.exporting = True
  186. self.export_state_changed()
  187. def export_complete(self, path):
  188. self.exporting = False
  189. self.export_state_changed()
  190. self.show_message("Export Complete!", "Your photo has been exported to '%s'" % path)
  191. def export_state_changed(self):
  192. self.ui["export_image"].set_sensitive(not self.exporting)
  193. self.ui["open_pf2"].set_sensitive(not self.exporting)
  194. self.ui["preview_button"].set_sensitive(not self.exporting)
  195. def export_clicked(self, sender):
  196. self.method = self.methodMap[self.ui["method"].get_active_text()]
  197. self.paths = self.get_paths()
  198. height, width = cv2.imread(self.paths[0]).shape[:2]
  199. Export.ExportDialog(self.root, self.builder, width, height, self.get_export_image,
  200. self.export_complete, self.paths[0])
  201. def export_pf2_clicked(self, sender):
  202. self.method = self.methodMap[self.ui["method"].get_active_text()]
  203. GLib.idle_add(self.export_started)
  204. self.paths = self.get_paths()
  205. threading.Thread(target=self.do_export_pf2).start()
  206. def do_export_pf2(self):
  207. export_path = "".join(self.paths[0].split(".")[:-1])
  208. export_path += "_HDR.tiff"
  209. height, width = cv2.imread(self.paths[0]).shape[:2]
  210. self.do_stack(self.paths, export_path, width, height)
  211. GLib.idle_add(self.export_pf2_complete, export_path)
  212. def export_pf2_complete(self, path):
  213. self.exporting = False
  214. self.export_state_changed()
  215. self.switch_activity("PF2", path)