__init__.py 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  1. import os
  2. import random
  3. import threading
  4. from gi.repository import GLib
  5. import cv2
  6. import subprocess
  7. from Export.exiftool import ExifTool
  8. class ExportDialog:
  9. def __init__(self, root, builder, w, h, get_image_call, done_call, path):
  10. self.exif = ExifTool()
  11. self.path = path
  12. self.builder = builder
  13. self.root = root
  14. # What gets called to get the image object at process time
  15. self.get_image_call = get_image_call
  16. self.done_call = done_call
  17. self.width = w
  18. self.height = h
  19. self.image = None
  20. UI_FILE = "ui/Export.glade"
  21. self.builder.add_from_file(UI_FILE)
  22. self.ui = {}
  23. components = [
  24. "window",
  25. "headerbar",
  26. "file",
  27. "save_button",
  28. "cancel_button",
  29. "preset",
  30. "format",
  31. "pngcrush",
  32. "width",
  33. "height",
  34. "quality",
  35. "width_spin",
  36. "height_spin",
  37. "quality_spin",
  38. "metadata",
  39. "description",
  40. "artist",
  41. "copyright"
  42. ]
  43. for component in components:
  44. self.ui[component] = self.builder.get_object("Export_%s" % (component))
  45. # Setup Export Dialog
  46. self.ui["window"].set_transient_for(self.root)
  47. self.ui["window"].set_titlebar(self.ui["headerbar"])
  48. self.ui["width"].set_upper(w)
  49. self.ui["width"].set_value(w)
  50. self.ui["height"].set_upper(h)
  51. self.ui["height"].set_value(h)
  52. self.ui["file"].set_filename(".".join(path.split(".")[:-1]) + "_PF")
  53. self.ui["file"].set_current_name(".".join(path.split("/")[-1:][0].split(".")[:-1]) + "_PF")
  54. # Connect siginals
  55. self.ui["preset"].connect("changed", self.on_preset_changed)
  56. self.ui["format"].connect("changed", self.on_format_changed)
  57. self.ui["width_spin"].connect("changed", self.on_width_changed)
  58. self.ui["height_spin"].connect("changed", self.on_height_changed)
  59. self.ui["save_button"].connect("clicked", self.on_save_clicked)
  60. self.ui["cancel_button"].connect("clicked", self.on_cancel_clicked)
  61. # Get EXIF data for this image ready
  62. self.exif.start()
  63. data = self.exif.get_metadata(self.path)
  64. # Preload editable fields
  65. self.ui["description"].set_text(self.safe_exif_get(data, "EXIF:ImageDescription"))
  66. self.ui["artist"].set_text(self.safe_exif_get(data, "EXIF:Artist"))
  67. self.ui["copyright"].set_text(self.safe_exif_get(data, "EXIF:Copyright"))
  68. print(self.safe_exif_get(data, "Flash"))
  69. self.ui["window"].show_all()
  70. def safe_exif_get(self, data, key):
  71. out = ""
  72. if(key in data):
  73. out = data[key]
  74. return out
  75. def on_preset_changed(self, sender):
  76. if(sender.get_active() == 0):
  77. self.ui["width_spin"].set_sensitive(True)
  78. self.ui["height_spin"].set_sensitive(True)
  79. self.ui["format"].set_sensitive(True)
  80. self.ui["pngcrush"].set_sensitive(self.ui["format"].get_active() == 0)
  81. self.ui["quality_spin"].set_sensitive(self.ui["format"].get_active() == 1)
  82. else:
  83. if(sender.get_active() == 1):
  84. self.ui["width"].set_value(2048)
  85. self.ui["format"].set_active(1)
  86. self.ui["quality"].set_value(85)
  87. elif(sender.get_active() == 2):
  88. self.ui["width"].set_value(self.width)
  89. self.ui["format"].set_active(0)
  90. self.ui["pngcrush"].set_active(True)
  91. elif(sender.get_active() == 3):
  92. self.ui["width"].set_value(1920)
  93. self.ui["format"].set_active(1)
  94. self.ui["quality"].set_value(67)
  95. elif(sender.get_active() == 4):
  96. self.ui["width"].set_value(self.width)
  97. self.ui["format"].set_active(2)
  98. self.ui["width_spin"].set_sensitive(False)
  99. self.ui["height_spin"].set_sensitive(False)
  100. self.ui["format"].set_sensitive(False)
  101. self.ui["pngcrush"].set_sensitive(False)
  102. self.ui["quality_spin"].set_sensitive(False)
  103. def on_format_changed(self, sender):
  104. self.ui["pngcrush"].set_sensitive(sender.get_active() == 0)
  105. self.ui["quality_spin"].set_sensitive(sender.get_active() == 1)
  106. self.ui["pngcrush"].set_active(False)
  107. def on_width_changed(self, sender):
  108. w = sender.get_value()
  109. r = w/self.width
  110. self.ui["height_spin"].set_value(r*self.height)
  111. def on_height_changed(self, sender):
  112. h = sender.get_value()
  113. r = h/self.height
  114. self.ui["width_spin"].set_value(r*self.width)
  115. def on_cancel_clicked(self, sender):
  116. self.exif.terminate()
  117. self.ui["window"].close()
  118. def on_save_clicked(self, sender):
  119. self.ui["window"].close()
  120. format = self.ui["format"].get_active()
  121. quality = self.ui["quality"].get_value()
  122. width = self.ui["width"].get_value()
  123. height = self.ui["height"].get_value()
  124. path = self.ui["file"].get_filename()
  125. pngcrush = self.ui["pngcrush"].get_active()
  126. # Metadata
  127. metadata_scheme = self.ui["metadata"].get_active()
  128. description = self.ui["description"].get_text()
  129. artist = self.ui["artist"].get_text()
  130. copyright_info = self.ui["copyright"].get_text()
  131. threading.Thread(target=self.do_export, args=(format, quality, width,
  132. height, path, pngcrush,
  133. metadata_scheme,
  134. description, artist,
  135. copyright_info)).start()
  136. def do_export(self, format, quality, width, height, path, pngcrush, metadata_scheme, description, artist, copyright_info):
  137. self.image = self.get_image_call(width, height)
  138. newPath = path
  139. if(format == 0) and (not path.endswith(".png")):
  140. newPath += ".png"
  141. if(format == 1) and (not path.endswith(".jpg")) and (not path.endswith(".jpeg")):
  142. newPath += ".jpg"
  143. if(format == 2) and (not path.endswith(".tif")) and (not path.endswith(".tiff")):
  144. newPath += ".tiff"
  145. if(format == 1):
  146. # Convert to 8 Bit
  147. bpp = int(str(self.image.dtype).replace("uint", ""))
  148. im = (self.image/float(2**bpp))*255
  149. cv2.imwrite(newPath, im, [int(cv2.IMWRITE_JPEG_QUALITY), int(quality)])
  150. elif(format == 0) and pngcrush:
  151. tempPath = "/tmp/pngcrush-%i.png" % random.randrange(1000000,9999999)
  152. cv2.imwrite(tempPath, self.image)
  153. subprocess.call(["pngcrush", "-rem gAMA", "-rem cHRM", "-rem iCCP", "-rem sRGB" , "-m 0", "-l 9", "-fix", "-v", "-v", tempPath, newPath])
  154. os.unlink(tempPath)
  155. else:
  156. cv2.imwrite(newPath, self.image)
  157. # Write metadata
  158. # If the scheme is Include Original Metadata
  159. if(metadata_scheme == 0):
  160. # Copy metadata from original file
  161. self.exif.execute(b"-TagsFromFile", self.path.encode("utf-8"), b"-all:all", newPath.encode("utf-8"), b"-overwrite_original")
  162. # Scheme 1 (Ignore Original Metadata) does not copy any data so is skipped here
  163. # If the scheme is Strip GPS Information
  164. if(metadata_scheme == 2):
  165. # Copy all metadata not pertaining to GPS information
  166. self.exif.execute(b"-tagsFromFile", self.path.encode("utf-8"), b"-all:all", b"-gps:all=", b"-xmp:geotag=", newPath.encode("utf-8"), b"-overwrite_original")
  167. # Write dialog editable metadata and application metadata to file
  168. self.exif.execute(self.fmt_exif_set(description, b"ImageDescription"),
  169. self.fmt_exif_set(artist, b"Artist"),
  170. self.fmt_exif_set(copyright_info, b"Copyright"),
  171. self.fmt_exif_set("PhotoFiddle2", b"Software"),
  172. newPath.encode("utf-8"), b"-overwrite_original")
  173. # Clean up
  174. self.exif.terminate()
  175. GLib.idle_add(self.done_call, newPath)
  176. def fmt_exif_set(self, data, key):
  177. return b"-%s=%s" % (key, data.encode("utf-8"))