controller.py 57 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281
  1. # uHDR: HDR image editing software
  2. # Copyright (C) 2021 remi cozot
  3. #
  4. # This program is free software: you can redistribute it and/or modify
  5. # it under the terms of the GNU General Public License as published by
  6. # the Free Software Foundation, either version 3 of the License, or
  7. # (at your option) any later version.
  8. #
  9. # This program is distributed in the hope that it will be useful,
  10. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. # GNU General Public License for more details.
  13. #
  14. # hdrCore project 2020
  15. # author: remi.cozot@univ-littoral.fr
  16. # -----------------------------------------------------------------------------
  17. # --- Package hdrGUI ---------------------------------------------------------
  18. # -----------------------------------------------------------------------------
  19. """
  20. package hdrGUI consists of the classes for GUI.
  21. """
  22. # -----------------------------------------------------------------------------
  23. # --- Import ------------------------------------------------------------------
  24. # -----------------------------------------------------------------------------
  25. import enum, sys, subprocess, copy, colour, time, os, shutil, datetime, ctypes
  26. import numpy as np
  27. # pyQT5 import
  28. from PyQt5.QtWidgets import QFileDialog, QApplication
  29. from PyQt5.QtWidgets import QMessageBox
  30. from . import model, view, thread
  31. import hdrCore.image, hdrCore.processing, hdrCore.utils
  32. import hdrCore.coreC
  33. import preferences.preferences as pref
  34. # zj add for semi-auto curve
  35. import torch
  36. from hdrCore.net import Net
  37. from torch.autograd import Variable
  38. # -----------------------------------------------------------------------------
  39. # --- package methods ---------------------------------------------------------
  40. # -----------------------------------------------------------------------------
  41. def getScreenSize(app):
  42. screens = app.screens()
  43. res = list(map(lambda x: x.size(), screens))
  44. return res
  45. # -----------------------------------------------------------------------------
  46. # --- Class GalleryMode -------------------------------------------------------
  47. # -----------------------------------------------------------------------------
  48. class GalleryMode(enum.Enum):
  49. """ Enum """
  50. _1x1 = 0 #
  51. _3x2 = 1 #
  52. _6x4 = 2 #
  53. _9x6 = 3 #
  54. _2x1 = 4
  55. def nbRow(m):
  56. if m == GalleryMode._1x1: return 1
  57. if m == GalleryMode._3x2: return 2
  58. if m == GalleryMode._6x4: return 4
  59. if m == GalleryMode._9x6: return 6
  60. if m == GalleryMode._2x1: return 1
  61. def nbCol(m):
  62. if m == GalleryMode._1x1: return 1
  63. if m == GalleryMode._3x2: return 3
  64. if m == GalleryMode._6x4: return 6
  65. if m == GalleryMode._9x6: return 9
  66. if m == GalleryMode._2x1: return 2
  67. # -----------------------------------------------------------------------------
  68. # --- Class ImageWidgetController ---------------------------------------------
  69. # -----------------------------------------------------------------------------
  70. class ImageWidgetController:
  71. """ image widget controller """
  72. def __init__(self, image=None,id = -1):
  73. self.model = model.ImageWidgetModel(self)
  74. self.view = view.ImageWidgetView(self)
  75. self._id = id # store an (unique) id
  76. if isinstance(image, (np.ndarray, hdrCore.image.Image)):
  77. self.model.setImage(image)
  78. self.view.setPixmap(self.model.getColorData())
  79. def setImage(self, image):
  80. self.model.setImage(image)
  81. return self.view.setPixmap(self.model.getColorData())
  82. def setQPixmap(self, qPixmap):
  83. self.view.setQPixmap(qPixmap)
  84. def id(self): return self._id
  85. # -----------------------------------------------------------------------------
  86. # --- Class ImageGalleryController --------------------------------------------
  87. # -----------------------------------------------------------------------------
  88. class ImageGalleryController():
  89. """ image gallery controller """
  90. def __init__(self, parent):
  91. if pref.verbose: print(" [CONTROL] >> ImageGalleryController.__init__()")
  92. self.parent = parent # AppView
  93. self.view = view.ImageGalleryView(self)
  94. self.model = model.ImageGalleryModel(self)
  95. def setImages(self, imageFiles): self.model.setImages(imageFiles)
  96. def updateImages(self):
  97. """ called by ImageGalleryModel to ask for update images """
  98. self.view.pageNumber = 0 # reset page number
  99. self.view.updateImages()
  100. def callBackButton_previousPage(self):
  101. self.view.changePageNumber(-1)
  102. if self.view.shapeMode == GalleryMode._1x1 : self.selectImage(0)
  103. def callBackButton_nextPage(self):
  104. self.view.changePageNumber(+1)
  105. if self.view.shapeMode == GalleryMode._1x1 : self.selectImage(0)
  106. def computePageNumberOnGalleryModeChange(self,newGalleryMode):
  107. currentPage = self.view.pageNumber
  108. nbImagePerPage = GalleryMode.nbRow(self.view.shapeMode)*GalleryMode.nbCol(self.view.shapeMode)
  109. selectedImage = self.model.selectedImage() if (self.model.selectedImage()!=-1) else currentPage*nbImagePerPage
  110. newNbImagePerPage = GalleryMode.nbRow(newGalleryMode)*GalleryMode.nbCol(newGalleryMode)
  111. newPageNumber = selectedImage//newNbImagePerPage
  112. return newPageNumber
  113. def callBackButton_1x1(self):
  114. if self.view.shapeMode != GalleryMode._1x1:
  115. self.view.resetGridLayoutWidgets()
  116. self.view.pageNumber = self.computePageNumberOnGalleryModeChange(GalleryMode._1x1)
  117. self.view.shapeMode = GalleryMode._1x1
  118. self.view.buildGridLayoutWidgets()
  119. self.view.updateImages()
  120. self.model.loadPage(self.view.pageNumber)
  121. self.view.repaint()
  122. def callBackButton_3x2(self):
  123. if self.view.shapeMode != GalleryMode._3x2:
  124. self.view.resetGridLayoutWidgets()
  125. self.view.pageNumber = self.computePageNumberOnGalleryModeChange(GalleryMode._3x2)
  126. self.view.shapeMode = GalleryMode._3x2
  127. self.view.buildGridLayoutWidgets()
  128. self.view.updateImages()
  129. self.model.loadPage(self.view.pageNumber)
  130. self.view.repaint()
  131. def callBackButton_6x4(self):
  132. if self.view.shapeMode != GalleryMode._6x4:
  133. self.view.resetGridLayoutWidgets()
  134. self.view.pageNumber = self.computePageNumberOnGalleryModeChange(GalleryMode._6x4)
  135. self.view.shapeMode = GalleryMode._6x4
  136. self.view.buildGridLayoutWidgets()
  137. self.view.updateImages()
  138. self.model.loadPage(self.view.pageNumber)
  139. self.view.repaint()
  140. def callBackButton_9x6(self):
  141. if self.view.shapeMode != GalleryMode._9x6:
  142. self.view.resetGridLayoutWidgets()
  143. self.view.pageNumber = self.computePageNumberOnGalleryModeChange(GalleryMode._9x6)
  144. self.view.shapeMode = GalleryMode._9x6
  145. self.view.buildGridLayoutWidgets()
  146. self.view.updateImages()
  147. self.model.loadPage(self.view.pageNumber)
  148. self.view.repaint()
  149. def callBackButton_2x1(self):
  150. if self.view.shapeMode != GalleryMode._2x1:
  151. self.view.resetGridLayoutWidgets()
  152. self.view.pageNumber = self.computePageNumberOnGalleryModeChange(GalleryMode._2x1)
  153. self.view.shapeMode = GalleryMode._2x1
  154. self.view.buildGridLayoutWidgets()
  155. self.view.updateImages()
  156. self.model.loadPage(self.view.pageNumber)
  157. self.view.repaint()
  158. def selectImage(self, id):
  159. if pref.verbose: print(" [CONTROL] >> ImageGalleryController.selectImage()")
  160. nbImagePage = GalleryMode.nbRow(self.view.shapeMode)*GalleryMode.nbCol(self.view.shapeMode)
  161. idxImage = self.view.pageNumber*nbImagePage+id
  162. # check id
  163. if (idxImage < len(self.model.processPipes)):
  164. # update selected image
  165. processPipe = self.model.processPipes[idxImage]
  166. if processPipe:
  167. if self.parent.dock.setProcessPipe(processPipe):
  168. self.model.setSelectedImage(idxImage)
  169. def getSelectedProcessPipe(self):
  170. if pref.verbose: print(" [CONTROL] >> ImageGalleryController.getSelectedProcessPipe()")
  171. return self.model.getSelectedProcessPipe()
  172. def setProcessPipeWidgetQPixmap(self, qPixmap):
  173. idxProcessPipe = self.model.selectedImage()
  174. nbImagePage = GalleryMode.nbRow(self.view.shapeMode)*GalleryMode.nbCol(self.view.shapeMode)
  175. idxImageWidget = idxProcessPipe%nbImagePage
  176. if pref.verbose: print(" >> ImageGalleryController.setProcessPipeWidgetQPixmap(...)[ image id:",idxProcessPipe,">> image widget controller:",idxImageWidget,"]")
  177. self.view.imagesControllers[idxImageWidget].setQPixmap(qPixmap)
  178. def save(self):
  179. if pref.verbose: print(" [CONTROL] >> ImageGalleryController.save()")
  180. self.model.save()
  181. def currentPage(self): return self.view.currentPage()
  182. def pageIdx(self):
  183. nb = self.currentPage()
  184. nbImagePage = GalleryMode.nbRow(self.view.shapeMode)*GalleryMode.nbCol(self.view.shapeMode)
  185. return (nb*nbImagePage), ((nb+1)*nbImagePage)
  186. def getFilenamesOfCurrentPage(self): return self.model.getFilenamesOfCurrentPage()
  187. def getProcessPipeById(self,i) : return self.model.getProcessPipeById(i)
  188. def getProcessPipes(self): return self.model.processPipes
  189. # -----------------------------------------------------------------------------
  190. # --- Class AppController -----------------------------------------------------
  191. # -----------------------------------------------------------------------------
  192. class AppController(object):
  193. """controller for MainWindow
  194. Attributes:
  195. screenSize
  196. hdrDisplay
  197. view
  198. model
  199. Methods:
  200. callBackSelectDir(self)
  201. callBackSave(self)
  202. callBackDisplayHDR(self)
  203. callBackEndDisplay(self, img)
  204. callBackCloseDisplayHDR(self)
  205. callBackCompareRawEditedHDR(self)
  206. callBackExportHDR(self)
  207. callBackEndExportHDR(self, img)
  208. callBackExportAllHDR(self)
  209. callBackEndAllExportHDR(self, img)
  210. """
  211. def __init__(self, app):
  212. if pref.verbose: print(" [CONTROL] >> AppController.__init__()")
  213. self.screenSize = getScreenSize(app)# get screens size
  214. # attributes
  215. self.hdrDisplay = HDRviewerController(self)
  216. self.view = view.AppView(self, HDRcontroller = self.hdrDisplay)
  217. self.model = model.AppModel(self)
  218. self.dirName = None
  219. self.imagesName = []
  220. self.view.show()
  221. # -----------------------------------------------------------------------------
  222. def callBackSelectDir(self):
  223. """Callback of export HDR menu: open file dialog, store image filenames (self.imagesName), set directory to model
  224. """
  225. if pref.verbose: print(" [CONTROL] >> AppController.callBackSelectDir()")
  226. dirName = QFileDialog.getExistingDirectory(None, 'Select Directory', self.model.directory)
  227. if dirName != "":
  228. # save current images (metadata)
  229. self.view.imageGalleryController.save()
  230. # get images in the selected directory
  231. self.imagesName = []; self.imagesName = list(self.model.setDirectory(dirName))
  232. self.view.imageGalleryController.setImages(self.imagesName)
  233. self.hdrDisplay.displaySplash()
  234. # -----------------------------------------------------------------------------
  235. def callBackSave(self): self.view.imageGalleryController.save()
  236. # -----------------------------------------------------------------------------
  237. def callBackQuit(self):
  238. if pref.verbose: print(" [CB] >> AppController.callBackQuit()")
  239. self.view.imageGalleryController.save()
  240. self.hdrDisplay.close()
  241. sys.exit()
  242. # -----------------------------------------------------------------------------
  243. def callBackDisplayHDR(self):
  244. if pref.verbose: print(" [CONTROL] >> AppController.callBackDisplayHDR()")
  245. selectedProcessPipe = self.view.imageGalleryController.model.getSelectedProcessPipe()
  246. if selectedProcessPipe:
  247. self.view.statusBar().showMessage('displaying HDR image, full size image computation: start, please wait !')
  248. self.view.statusBar().repaint()
  249. # save current processpipe metada
  250. originalImage = copy.deepcopy(selectedProcessPipe.originalImage)
  251. originalImage.metadata.metadata['processpipe'] = selectedProcessPipe.toDict()
  252. originalImage.metadata.save()
  253. # load full size image
  254. img = hdrCore.image.Image.read(originalImage.path+'/'+originalImage.name)
  255. # turn off: autoResize
  256. hdrCore.processing.ProcessPipe.autoResize = False
  257. # make a copy of selectedProcessPipe
  258. processpipe = copy.deepcopy(selectedProcessPipe)
  259. # set size to display size
  260. size = pref.getDisplayShape()
  261. img = img.process(hdrCore.processing.resize(),size=(None, size[1]))
  262. # set image to process-pipe
  263. processpipe.setImage(img)
  264. thread.cCompute(self.callBackEndDisplay, processpipe, toneMap=False, progress=self.view.statusBar().showMessage)
  265. # -----------------------------------------------------------------------------
  266. def callBackEndDisplay(self, img):
  267. if pref.verbose: print(" [CONTROL] >> AppController.callBackEndDisplay()")
  268. # turn off: autoResize
  269. hdrCore.processing.ProcessPipe.autoResize = True
  270. self.view.statusBar().showMessage('displaying HDR image, full size image computation: done !')
  271. # clip, scale
  272. img = img.process(hdrCore.processing.clip())
  273. img.colorData = img.colorData*pref.getDisplayScaling()
  274. colour.write_image(img.colorData,"temp.hdr", method='Imageio') # local copy for display
  275. self.hdrDisplay.displayFile("temp.hdr")
  276. # -----------------------------------------------------------------------------
  277. def callBackCloseDisplayHDR(self):
  278. if pref.verbose: print(" [CONTROL] >> AppController.callBackCloseDisplayHDR()")
  279. self.hdrDisplay.displaySplash()
  280. # -----------------------------------------------------------------------------
  281. def callBackCompareRawEditedHDR(self):
  282. """
  283. Callback of compare raw/edited HDR menu
  284. Display side by side original image and edited version.
  285. """
  286. if pref.verbose: print(" [CONTROL] >> AppController.callBackCompareOriginalInputHDR()")
  287. # process real size image
  288. # get selected process pipe
  289. selectedProcessPipe = self.view.imageGalleryController.model.getSelectedProcessPipe()
  290. if selectedProcessPipe: # check if a process pipe is selected
  291. # read original image
  292. img = hdrCore.image.Image.read(selectedProcessPipe.originalImage.path+'/'+selectedProcessPipe.originalImage.name)
  293. # resize
  294. screenY, screenX = pref.getDisplayShape()
  295. imgY, imgX,_ = img.shape
  296. marginY = int((screenY - imgY/2)/2)
  297. marginX = int(marginY/4)
  298. imgXp = int((screenX - 3*marginX)/2)
  299. img = img.process(hdrCore.processing.resize(),size=(None,imgXp))
  300. imgY, imgX, _ = img.shape
  301. # original image after resize
  302. ori = copy.deepcopy(img)
  303. # build process pipe from selected one them compute
  304. pp = hdrCore.processing.ProcessPipe()
  305. hdrCore.processing.ProcessPipe.autoResize = False # stop autoResize
  306. params= []
  307. for p in selectedProcessPipe.processNodes:
  308. pp.append(copy.deepcopy(p.process),paramDict=None, name=copy.deepcopy(p.name))
  309. params.append({p.name:p.params})
  310. img.metadata.metadata['processpipe'] = params
  311. pp.setImage(img)
  312. res = hdrCore.coreC.coreCcompute(img, pp)
  313. res = res.process(hdrCore.processing.clip())
  314. imgYres, imgXres, _ = res.colorData.shape
  315. hdrCore.processing.ProcessPipe.autoResize = True # return to autoResize
  316. # make comparison image
  317. oriColorData = ori.colorData*pref.getDisplayScaling()
  318. resColorData = res.colorData*pref.getDisplayScaling()
  319. display = np.ones((screenY,screenX,3))*0.2
  320. marginY = int((screenY - imgY)/2)
  321. marginYres = int((screenY - imgYres)/2)
  322. display[marginY:marginY+imgY, marginX:marginX+imgX,:] = oriColorData
  323. display[marginYres:marginYres+imgYres, 2*marginX+imgX:2*marginX+imgX+imgXres,:] = resColorData
  324. # save as compOrigFinal.hdr
  325. colour.write_image(display,'compOrigFinal.hdr', method='Imageio')
  326. self.hdrDisplay.displayFile('compOrigFinal.hdr')
  327. # -----------------------------------------------------------------------------
  328. def callBackExportHDR(self):
  329. """
  330. Callback of export HDR menu
  331. Export the image associated to selected process pipe
  332. """
  333. if pref.verbose: print(" [CONTROL] >> AppController.callBackExportHDR()")
  334. selectedProcessPipe = self.view.imageGalleryController.model.getSelectedProcessPipe()
  335. if selectedProcessPipe:
  336. # select dir where to save export
  337. self.dirName = QFileDialog.getExistingDirectory(None, 'Select Directory where to export HDR file', self.model.directory)
  338. # show export message
  339. self.view.statusBar().showMessage('exporting HDR image ('+pref.getHDRdisplay()['tag']+'), full size image computation: start, please wait !')
  340. self.view.statusBar().repaint()
  341. # save current processpipe metada
  342. originalImage = copy.deepcopy(selectedProcessPipe.originalImage)
  343. originalImage.metadata.metadata['processpipe'] = selectedProcessPipe.toDict()
  344. originalImage.metadata.save()
  345. # load full size image
  346. img = hdrCore.image.Image.read(originalImage.path+'/'+originalImage.name)
  347. # turn off: autoResize
  348. hdrCore.processing.ProcessPipe.autoResize = False
  349. # make a copy of selectedProcessPipe
  350. processpipe = copy.deepcopy(selectedProcessPipe)
  351. # set image to process-pipe
  352. processpipe.setImage(img)
  353. thread.cCompute(self.callBackEndExportHDR, processpipe, toneMap=False, progress=self.view.statusBar().showMessage)
  354. # -----------------------------------------------------------------------------
  355. def callBackEndExportHDR(self, img):
  356. # turn off: autoResize
  357. hdrCore.processing.ProcessPipe.autoResize = True
  358. self.view.statusBar().showMessage('exporting HDR image ('+pref.getHDRdisplay()['tag']+'), full size image computation: done !')
  359. # clip, scale
  360. img = img.process(hdrCore.processing.clip())
  361. img.colorData = img.colorData*pref.getDisplayScaling()
  362. if self.dirName:
  363. pathExport = os.path.join(self.dirName, img.name[:-4]+pref.getHDRdisplay()['post']+'.hdr')
  364. img.type = hdrCore.image.imageType.HDR
  365. img.metadata.metadata['processpipe'] = None
  366. img.metadata.metadata['display'] = pref.getHDRdisplay()['tag']
  367. img.write(pathExport)
  368. colour.write_image(img.colorData,"temp.hdr", method='Imageio') # local copy for display
  369. self.hdrDisplay.displayFile("temp.hdr")
  370. # -----------------------------------------------------------------------------
  371. def callBackExportAllHDR(self):
  372. if pref.verbose: print(" [CONTROL] >> AppController.callBackExportAllHDR()")
  373. self.processPipes = self.view.imageGalleryController.getProcessPipes()
  374. # select dir where to save export
  375. self.dirName = QFileDialog.getExistingDirectory(None, 'Select Directory where to export HDR file', self.model.directory)
  376. self.view.statusBar().showMessage('exporting '+str(len(self.processPipes))+' HDR images ... please wait')
  377. self.view.statusBar().repaint()
  378. self.imageToExport = len(self.processPipes) ; self.imageExportDone = 0
  379. pp = self.processPipes[0]
  380. # save current processpipe metada
  381. originalImage = copy.deepcopy(pp.originalImage)
  382. originalImage.metadata.metadata['processpipe'] = pp.toDict()
  383. originalImage.metadata.save()
  384. # load full size image
  385. img = hdrCore.image.Image.read(originalImage.path+'/'+originalImage.name)
  386. # turn off: autoResize
  387. hdrCore.processing.ProcessPipe.autoResize = False
  388. # make a copy of selectedProcessPipe
  389. processpipe = copy.deepcopy(pp)
  390. # set image to process-pipe
  391. processpipe.setImage(img)
  392. thread.cCompute(self.callBackEndAllExportHDR, processpipe, toneMap=False, progress=self.view.statusBar().showMessage)
  393. # -----------------------------------------------------------------------------
  394. def callBackEndAllExportHDR(self, img):
  395. # last image ?
  396. self.imageExportDone +=1
  397. self.view.statusBar().showMessage('exporting HDR images ('+pref.getHDRdisplay()['tag']+'):'+str(int(100*self.imageExportDone/self.imageToExport))+'% done !')
  398. # clip, scale
  399. img = img.process(hdrCore.processing.clip())
  400. img.colorData = img.colorData*pref.getDisplayScaling()
  401. if self.dirName:
  402. pathExport = os.path.join(self.dirName, img.name[:-4]+pref.getHDRdisplay()['post']+'.hdr')
  403. img.type = hdrCore.image.imageType.HDR
  404. img.metadata.metadata['processpipe'] = None
  405. img.metadata.metadata['display'] = pref.getHDRdisplay()['tag']
  406. img.write(pathExport)
  407. if self.imageExportDone == self.imageToExport :
  408. # turn off: autoResize
  409. hdrCore.processing.ProcessPipe.autoResize = True
  410. else:
  411. pp = self.processPipes[self.imageExportDone]
  412. if not pp:
  413. img = hdrCore.image.Image.read(self.imagesName[self.imageExportDone], thumb=True)
  414. pp = model.EditImageModel.buildProcessPipe()
  415. pp.setImage(img)
  416. # save current processpipe metada
  417. originalImage = copy.deepcopy(pp.originalImage)
  418. originalImage.metadata.metadata['processpipe'] = pp.toDict()
  419. originalImage.metadata.save()
  420. # load full size image
  421. img = hdrCore.image.Image.read(originalImage.path+'/'+originalImage.name)
  422. # turn off: autoResize
  423. hdrCore.processing.ProcessPipe.autoResize = False
  424. # make a copy of selectedProcessPipe
  425. processpipe = copy.deepcopy(pp)
  426. # set image to process-pipe
  427. processpipe.setImage(img)
  428. thread.cCompute(self.callBackEndAllExportHDR, processpipe, toneMap=False, progress=self.view.statusBar().showMessage)
  429. # ------------------------------------------------------------------------------------------
  430. # --- class MultiDockController() ----------------------------------------------------------
  431. # ------------------------------------------------------------------------------------------
  432. class MultiDockController():
  433. def __init__(self,parent=None, HDRcontroller = None):
  434. if pref.verbose: print(" [CONTROL] >> MultiDockController.__init__()")
  435. self.parent = parent
  436. self.view = view.MultiDockView(self, HDRcontroller)
  437. self.model = None
  438. # ---------------------------------------------------------------------------------------
  439. def activateEDIT(self): self.switch(0)
  440. def activateINFO(self): self.switch(1)
  441. def activateMIAM(self): self.switch(2)
  442. # ---------------------------------------------------------------------------------------
  443. def switch(self,nb):
  444. if pref.verbose: print(" [CONTROL] >> MultiDockController.switch()")
  445. self.view.switch(nb)
  446. # --------------------------------------------------------------------------------------
  447. def setProcessPipe(self, processPipe):
  448. if pref.verbose: print(" [CONTROL] >> MultiDockController.setProcessPipe(",processPipe.getImage().name,")")
  449. return self.view.setProcessPipe(processPipe)
  450. # ------------------------------------------------------------------------------------------
  451. # ------------------------------------------------------------------------------------------
  452. # ------------------------------------------------------------------------------------------
  453. class EditImageController:
  454. def __init__(self, parent=None, HDRcontroller = None):
  455. if pref.verbose: print(" [CONTROL] >> EditImageController.__init__(",")")
  456. self.parent = parent
  457. self.previewHDR = True
  458. self.controllerHDR = HDRcontroller
  459. self.view = view.EditImageView(self)
  460. self.model = model.EditImageModel(self)
  461. # -----------------------------------------------------------------------------
  462. def setProcessPipe(self, processPipe):
  463. if pref.verbose: print(" [CONTROL] >> EditImageController.setProcessPipe(",")")
  464. if self.model.setProcessPipe(processPipe):
  465. # update view
  466. self.view.setProcessPipe(processPipe)
  467. self.view.imageWidgetController.setImage(processPipe.getImage())
  468. self.view.plotToneCurve()
  469. # update hdr viewer
  470. self.controllerHDR.displaySplash()
  471. return True
  472. else:
  473. return False
  474. # -----------------------------------------------------------------------------
  475. def getProcessPipe(self) : return self.model.getProcessPipe()
  476. # -----------------------------------------------------------------------------
  477. def buildView(self,processPipe=None):
  478. if pref.verbose: print(" [CONTROL] >> EditImageController.buildView(",")")
  479. """ called when MultiDockController recall a controller/view """
  480. self.view = view.EditImageView(self, build=True)
  481. if processPipe: self.setProcessPipe(processPipe)
  482. # -----------------------------------------------------------------------------
  483. def autoExposure(self):
  484. if pref.verbose: print(" [CONTROL] >> EditImageController.autoExposure(",")")
  485. if self.model.processpipe:
  486. img = self.model.autoExposure()
  487. # update view with computed EV
  488. paramDict = self.model.getEV()
  489. self.view.exposure.setValue(paramDict['EV'])
  490. qPixmap = self.view.setImage(img)
  491. self.parent.controller.parent.controller.view.imageGalleryController.setProcessPipeWidgetQPixmap(qPixmap)
  492. # -----------------------------------------------------------------------------
  493. def changeExposure(self,value):
  494. if pref.verbose: print(" [CONTROL] >> EditImageController.changeExposure(",value,")")
  495. if self.model.processpipe: self.model.changeExposure(value)
  496. # -----------------------------------------------------------------------------
  497. def changeContrast(self,value):
  498. if pref.verbose: print(" [CONTROL] >> EditImageController.changeContrast(",value,")")
  499. if self.model.processpipe: self.model.changeContrast(value)
  500. # -----------------------------------------------------------------------------
  501. def changeToneCurve(self,controlPoints):
  502. if pref.verbose: print(" [CONTROL] >> EditImageController.changeToneCurve("")")
  503. if self.model.processpipe: self.model.changeToneCurve(controlPoints)
  504. # -----------------------------------------------------------------------------
  505. def changeLightnessMask(self, maskValues):
  506. if pref.verbose: print(" [CONTROL] >> EditImageController.changeLightnessMask(",maskValues,")")
  507. if self.model.processpipe: self.model.changeLightnessMask(maskValues)
  508. # -----------------------------------------------------------------------------
  509. def changeSaturation(self,value):
  510. if pref.verbose: print(" [CONTROL] >> EditImageController.changeSaturation(",value,")")
  511. if self.model.processpipe: self.model.changeSaturation(value)
  512. # -----------------------------------------------------------------------------
  513. def changeColorEditor(self,values, idName):
  514. if pref.verbose: print(" [CONTROL] >> EditImageController.changeColorEditor(",values,")")
  515. if self.model.processpipe: self.model.changeColorEditor(values, idName)
  516. # -----------------------------------------------------------------------------
  517. def changeGeometry(self,values):
  518. if pref.verbose: print(" [CONTROL] >> EditImageController.changeGeometry(",values,")")
  519. if self.model.processpipe: self.model.changeGeometry(values)
  520. # -----------------------------------------------------------------------------
  521. def updateImage(self,imgTM):
  522. """
  523. updateImage: called when process-pipe computation is done
  524. Args:
  525. imgTM (hdrCoreimage.Image, required): tone mapped image (resized) for GUI display
  526. """
  527. qPixmap = self.view.setImage(imgTM)
  528. self.parent.controller.parent.controller.view.imageGalleryController.setProcessPipeWidgetQPixmap(qPixmap)
  529. self.view.plotToneCurve()
  530. # if aesthetics model > notify required update
  531. if self.previewHDR and self.model.autoPreviewHDR:
  532. self.controllerHDR.callBackUpdate()
  533. # ------------------------------------------------------------------------------------------
  534. # ------------------------------------------------------------------------------------------
  535. # ------------------------------------------------------------------------------------------
  536. class ImageInfoController:
  537. def __init__(self, parent=None):
  538. if pref.verbose: print(" [CONTROL] >> ImageInfoController.__init__()")
  539. self.parent = parent
  540. self.view = view.ImageInfoView(self)
  541. self.model = model.ImageInfoModel(self)
  542. self.callBackActive = True
  543. # -----------------------------------------------------------------------------
  544. def setProcessPipe(self, processPipe):
  545. if pref.verbose: print(" [CONTROL] >> ImageInfoController.setProcessPipe(",processPipe.getImage().name,")")
  546. self.model.setProcessPipe(processPipe)
  547. self.view.setProcessPipe(processPipe)
  548. return True
  549. # -----------------------------------------------------------------------------
  550. def buildView(self,processPipe=None):
  551. if pref.verbose: print(" [CONTROL] >> ImageInfoController.buildView()")
  552. """ called when MultiDockController recall a controller/view """
  553. self.view = view.ImageInfoView(self)
  554. if processPipe: self.setProcessPipe(processPipe)
  555. # -----------------------------------------------------------------------------
  556. def metadataChange(self,metaGroup,metaTag, on_off):
  557. if pref.verbose: print(" [CONTROL] >> ImageInfoController.useCaseChange(",metaGroup,",", metaTag,",", on_off,")")
  558. self.model.changeMeta(metaGroup,metaTag, on_off)
  559. # ------------------------------------------------------------------------------------------
  560. # ------------------------------------------------------------------------------------------
  561. # ------------------------------------------------------------------------------------------
  562. class AdvanceSliderController():
  563. def __init__(self, parent,name, defaultValue, range, step,callBackValueChange=None,callBackAutoPush= None):
  564. if pref.verbose: print(" [CONTROL] >> AdvanceSliderController.__init__(",") ")
  565. self.parent = parent
  566. self.view = view.AdvanceSliderView(self, name, defaultValue, range, step)
  567. self.model = model.AdvanceSliderModel(self, value=defaultValue)
  568. self.step = step
  569. self.defaultValue = defaultValue
  570. self.range = range
  571. self.callBackActive = True
  572. self.callBackValueChange = callBackValueChange
  573. self.callBackAutoPush = callBackAutoPush
  574. # -----------------------------------------------------------------------------
  575. def sliderChange(self):
  576. value = self.view.slider.value()*self.step
  577. if pref.verbose: print(" [CB] >> AdvanceSliderController.sliderChange(",value,")[callBackActive:",self.callBackActive,"] ")
  578. self.model.value = value
  579. self.view.editValue.setText(str(value))
  580. if self.callBackActive and self.callBackValueChange: self.callBackValueChange(value)
  581. # -----------------------------------------------------------------------------
  582. def setValue(self, value, callBackActive = True):
  583. if pref.verbose: print(" [CONTROL] >> AdvanceSliderController.setValue(",value,") ")
  584. """ set value value in 'model' range"""
  585. self.callBackActive = callBackActive
  586. self.view.slider.setValue(int(value/self.step))
  587. self.view.editValue.setText(str(value))
  588. self.model.setValue(int(value))
  589. self.callBackActive = True
  590. # -----------------------------------------------------------------------------
  591. def reset(self):
  592. if pref.verbose : print(" [CB] >> AdvanceSliderController.reset(",") ")
  593. self.setValue(self.defaultValue,callBackActive = False)
  594. if self.callBackValueChange: self.callBackValueChange(self.defaultValue)
  595. # -----------------------------------------------------------------------------
  596. def auto(self):
  597. if pref.verbose: print(" [CB] >> AdvanceSliderController.auto(",") ")
  598. if self.callBackAutoPush: self.callBackAutoPush()
  599. # ------------------------------------------------------------------------------------------
  600. # --- class AdvanceSliderController --------------------------------------------------------
  601. # ------------------------------------------------------------------------------------------
  602. class ToneCurveController():
  603. def __init__(self, parent):
  604. self.parent = parent
  605. self.model = model.ToneCurveModel()
  606. self.view = view.ToneCurveView(self)
  607. self.callBackActive = True
  608. # tone curve display control
  609. self.showInput =False
  610. self.showbefore = False
  611. self.showAfter = False
  612. self.showOutput = True
  613. # zj add semi-auto curve
  614. # machine learning network and weight file
  615. self.weightFile = 'MSESig505_0419.pth'
  616. self.networkModel = None
  617. # -----------------------------------------------------------------------------
  618. def sliderChange(self, key, value):
  619. if pref.verbose: print(" [CB] >> ToneCurveController.sliderChange(",key,",",value,")[callBackActive:",self.callBackActive,"] ")
  620. if self.callBackActive:
  621. newValues = self.model.setValue(key, value, autoScale = False)
  622. self.parent.controller.changeToneCurve(newValues)
  623. points = self.model.evaluate()
  624. self.callBackActive = False
  625. self.view.sliderShadows.setValue(int(newValues["shadows"][1]))
  626. self.view.editShadows.setText(str(newValues["shadows"][1]))
  627. self.view.sliderBlacks.setValue(int(newValues["blacks"][1]))
  628. self.view.editBlacks.setText(str(newValues["blacks"][1]))
  629. self.view.sliderMediums.setValue(int(newValues["mediums"][1]))
  630. self.view.editMediums.setText(str(newValues["mediums"][1]))
  631. self.view.sliderWhites.setValue(int(newValues["whites"][1]))
  632. self.view.editWhites.setText(str(newValues["whites"][1]))
  633. self.view.sliderHighlights.setValue(int(newValues["highlights"][1]))
  634. self.view.editHighlights.setText(str(newValues["highlights"][1]))
  635. self.callBackActive = True
  636. # -----------------------------------------------------------------------------
  637. def setValues(self, valuesDict,callBackActive = False):
  638. if pref.verbose: print(" [CONTROL] >> ToneCurveController.setValue(",valuesDict,") ")
  639. self.callBackActive = callBackActive
  640. self.model.setValues(valuesDict)
  641. points = self.model.evaluate()
  642. self.view.sliderShadows.setValue(int(valuesDict["shadows"][1]))
  643. self.view.editShadows.setText(str(valuesDict["shadows"][1]))
  644. self.view.sliderBlacks.setValue(int(valuesDict["blacks"][1]))
  645. self.view.editBlacks.setText(str(valuesDict["blacks"][1]))
  646. self.view.sliderMediums.setValue(int(valuesDict["mediums"][1]))
  647. self.view.editMediums.setText(str(valuesDict["mediums"][1]))
  648. self.view.sliderWhites.setValue(int(valuesDict["whites"][1]))
  649. self.view.editWhites.setText(str(valuesDict["whites"][1]))
  650. self.view.sliderHighlights.setValue(int(valuesDict["highlights"][1]))
  651. self.view.editHighlights.setText(str(valuesDict["highlights"][1]))
  652. self.callBackActive = True
  653. # -----------------------------------------------------------------------------
  654. # zj add for semi-auto curve begin
  655. def autoCurve(self):
  656. processPipe = self.parent.controller.model.getProcessPipe()
  657. if processPipe != None :
  658. idExposure = processPipe.getProcessNodeByName("tonecurve")
  659. bins = np.linspace(0,1,50+1)
  660. imageBeforeColorData = processPipe.processNodes[idExposure-1].outputImage.colorData
  661. imageBeforeColorData[imageBeforeColorData>1]=1
  662. imageBeforeY = colour.sRGB_to_XYZ(imageBeforeColorData, apply_cctf_decoding=False)[:,:,1]
  663. nphistBefore = np.histogram(imageBeforeY, bins)[0]
  664. nphistBefore = nphistBefore/np.amax(nphistBefore)
  665. npImgHistCumuNorm = np.empty_like(nphistBefore)
  666. npImgHistCumu = np.cumsum(nphistBefore)
  667. npImgHistCumuNorm = npImgHistCumu/np.max(npImgHistCumu)
  668. #predict keypoint value
  669. if self.networkModel == None:
  670. self.networkModel = Net(50,5)
  671. self.networkModel.load_state_dict(torch.load(self.weightFile))
  672. self.networkModel.eval()
  673. with torch.no_grad():
  674. x = Variable(torch.FloatTensor([npImgHistCumuNorm.tolist(),]), requires_grad=True)
  675. y_predict = self.networkModel(x)
  676. kpc = (y_predict[0]*100).tolist()
  677. kpcDict = {'start':[0.0,0.0], 'shadows': [10.0,kpc[0]], 'blacks': [30.0,kpc[1]], 'mediums': [50.0,kpc[2]], 'whites': [70.0,kpc[3]], 'highlights': [90.0,kpc[4]], 'end': [100.0,100.0]}
  678. self.setValues(kpcDict,callBackActive = True)
  679. self.parent.controller.changeToneCurve(kpcDict)
  680. # zj add for semi-auto curve end
  681. # -----------------------------------------------------------------------------
  682. def reset(self, key):
  683. if pref.verbose: print(" [CONTROL] >> ToneCurveController.reset(",key,") ")
  684. valuesDefault = copy.deepcopy(self.model.default[key])[1]
  685. controls = self.model.setValue(key, valuesDefault)
  686. self.setValues(controls,callBackActive = False)
  687. self.parent.controller.changeToneCurve(controls)
  688. # -----------------------------------------------------------------------------
  689. def plotCurve(self):
  690. try:
  691. self.view.curve.plot([0,100],[0,100],'r--', clear=True)
  692. self.view.curve.plot([20,20],[0,100],'r--', clear=False)
  693. self.view.curve.plot([40,40],[0,100],'r--', clear=False)
  694. self.view.curve.plot([60,60],[0,100],'r--', clear=False)
  695. self.view.curve.plot([80,80],[0,100],'r--', clear=False)
  696. processPipe = self.parent.controller.model.getProcessPipe()
  697. idExposure = processPipe.getProcessNodeByName("tonecurve")
  698. bins = np.linspace(0,1,50+1)
  699. if self.showInput:
  700. imageInput = copy.deepcopy(processPipe.getInputImage())
  701. if imageInput.linear: imageInputColorData =colour.cctf_encoding(imageInput.colorData, function='sRGB')
  702. else: imageInputColorData = imageInput.colorData
  703. imageInputColorData[imageInputColorData>1]=1
  704. imageInputY = colour.sRGB_to_XYZ(imageInputColorData, apply_cctf_decoding=False)[:,:,1]
  705. nphistInput = np.histogram(imageInputY, bins)[0]
  706. nphistInput = nphistInput/np.amax(nphistInput)
  707. self.view.curve.plot(bins[:-1]*100,nphistInput*100,'k--', clear=False)
  708. if self.showbefore:
  709. imageBeforeColorData = processPipe.processNodes[idExposure-1].outputImage.colorData
  710. imageBeforeColorData[imageBeforeColorData>1]=1
  711. imageBeforeY = colour.sRGB_to_XYZ(imageBeforeColorData, apply_cctf_decoding=False)[:,:,1]
  712. nphistBefore = np.histogram(imageBeforeY, bins)[0]
  713. nphistBefore = nphistBefore/np.amax(nphistBefore)
  714. self.view.curve.plot(bins[:-1]*100,nphistBefore*100,'b--', clear=False)
  715. if self.showAfter:
  716. imageAftercolorData = processPipe.processNodes[idExposure].outputImage.colorData
  717. imageAftercolorData[imageAftercolorData>1]=1
  718. imageAfterY = colour.sRGB_to_XYZ(imageAftercolorData, apply_cctf_decoding=False)[:,:,1]
  719. nphistAfter = np.histogram(imageAfterY, bins)[0]
  720. nphistAfter =nphistAfter/np.amax(nphistAfter)
  721. self.view.curve.plot(bins[:-1]*100,nphistAfter*100,'b', clear=False)
  722. if self.showOutput:
  723. imageAftercolorData = processPipe.getImage(toneMap=True).colorData
  724. imageAftercolorData[imageAftercolorData>1]=1
  725. imageAfterY = colour.sRGB_to_XYZ(imageAftercolorData, apply_cctf_decoding=False)[:,:,1]
  726. nphistAfter = np.histogram(imageAfterY, bins)[0]
  727. nphistAfter =nphistAfter/np.amax(nphistAfter)
  728. self.view.curve.plot(bins[:-1]*100,nphistAfter*100,'b', clear=False)
  729. controlPointCoordinates= np.asarray(list(self.model.control.values()))
  730. self.view.curve.plot(controlPointCoordinates[1:-1,0],controlPointCoordinates[1:-1,1],'ro', clear=False)
  731. points = np.asarray(self.model.curve.evalpts)
  732. x = points[:,0]
  733. self.view.curve.plot(points[x<100,0],points[x<100,1],'r',clear=False)
  734. except:
  735. time.sleep(0.5)
  736. self.plotCurve()
  737. # ------------------------------------------------------------------------------------------
  738. # ------------------------------------------------------------------------------------------
  739. # ------------------------------------------------------------------------------------------
  740. class LightnessMaskController():
  741. def __init__(self, parent):
  742. if pref.verbose: print(" [CONTROL] >> MaskLightnessController.__init__(",")")
  743. self.parent= parent
  744. self.model = model.LightnessMaskModel(self)
  745. self.view = view.LightnessMaskView(self)
  746. self.callBackActive = True
  747. # -----------------------------------------------------------------------------
  748. def maskChange(self,key, on_off):
  749. if pref.verbose: print(" [CB] >> MaskLightnessController.maskChange(",key,",",on_off,")[callBackActive:",self.callBackActive,"] ")
  750. maskState = self.model.maskChange(key, on_off)
  751. self.parent.controller.changeLightnessMask(maskState)
  752. # -----------------------------------------------------------------------------
  753. def setValues(self, values,callBackActive = False):
  754. if pref.verbose: print(" [CONTROL] >> LightnessMaskController.setValue(",values,") ")
  755. self.callBackActive = callBackActive
  756. self.model.setValues(values)
  757. self.view.checkboxShadows.setChecked(values["shadows"])
  758. self.view.checkboxBlacks.setChecked(values["blacks"])
  759. self.view.checkboxMediums.setChecked(values["mediums"])
  760. self.view.checkboxWhites.setChecked(values["whites"])
  761. self.view.checkboxHighlights.setChecked(values["highlights"])
  762. self.callBackActive = True
  763. # ------------------------------------------------------------------------------------------
  764. # ------------------------------------------------------------------------------------------
  765. # ------------------------------------------------------------------------------------------
  766. class HDRviewerController():
  767. def __init__(self, parent):
  768. if pref.verbose: print(" [CONTROL] >> HDRviewerController.__init__(",")")
  769. self.parent= parent
  770. self.model = model.HDRviewerModel(self)
  771. self.view = None
  772. self.viewerProcess = None
  773. self.displaySplash()
  774. def setView(self, view): self.view = view
  775. def callBackUpdate(self):
  776. selectedProcessPipe = self.parent.view.imageGalleryController.model.getSelectedProcessPipe()
  777. img = selectedProcessPipe.getImage(toneMap = False)
  778. self.displayIMG(img)
  779. self.model.currentIMG = img
  780. def callBackAuto(self,on_off):
  781. self.parent.view.dock.view.childControllers[0].model.autoPreviewHDR = on_off
  782. def callBackCompare(self):
  783. if self.model.currentIMG:
  784. old = self.model.currentIMG
  785. old = old.process(hdrCore.processing.clip())
  786. sp = self.parent.view.imageGalleryController.model.getSelectedProcessPipe()
  787. img = sp.getImage(toneMap = False)
  788. img = img.process(hdrCore.processing.clip())
  789. h1, w1, _ = old.colorData.shape
  790. h2, w2, _ = img.colorData.shape
  791. hD, wD = self.model.displayModel['shape']
  792. hM = int((hD - max(h1,h2))/2)
  793. wM = int((wD - (w1+w2))/3)
  794. back = np.ones((hD,wD,3))*0.2
  795. back[hM:hM+h1,wM:wM+w1,:] = old.colorData*self.model.displayModel['scaling']
  796. back[hM:hM+h2,2*wM+w1:2*wM+w1+w2,:] = img.colorData*self.model.displayModel['scaling']
  797. # save as temp.hdr
  798. colour.write_image(back,'temp.hdr', method='Imageio')
  799. self.displayFile('temp.hdr')
  800. self.model.currentIMG = img
  801. else: self.callBackUpdate()
  802. def displayFile(self, HDRfilename):
  803. """
  804. HDRviewerController display file: run HDRImageViewer process ti display HDR image (from filename)
  805. Args:
  806. HDRfilename: string
  807. Required : hdr image filename
  808. """
  809. # check that no current display process already open
  810. if self.viewerProcess:
  811. # the display HDR process is already running
  812. # close current
  813. subprocess.run(['taskkill', '/F', '/T', '/IM', "HDRImageViewer*"],capture_output=False)
  814. time.sleep(0.05)
  815. # run display HDR process
  816. self.viewerProcess = subprocess.Popen(["HDRImageViewer.exe","-f", "-input:"+HDRfilename, "-f", "-h"], shell=True)
  817. time.sleep(0.10)
  818. psData = subprocess.run(['tasklist'], capture_output=True, universal_newlines=True).stdout
  819. if not 'HDRImageViewer' in psData:
  820. # re-run display HDR process
  821. self.viewerProcess = subprocess.Popen(["HDRImageViewer.exe","-f", "-input:"+HDRfilename, "-f", "-h"], shell=True)
  822. def displayIMG(self, img):
  823. img = img.process(hdrCore.processing.clip())
  824. colorData = img.colorData*self.model.displayModel['scaling']
  825. h,w, _ = colorData.shape
  826. hD, wD = self.model.displayModel['shape']
  827. if w<wD:
  828. back = np.ones((hD,wD,3))*0.2
  829. marginW = int((wD-w)/2)
  830. marginH = int((hD-h)/2)
  831. back[marginH:marginH+h,marginW:marginW+w,:]=colorData
  832. # save as temp.hdr
  833. colour.write_image(back,'temp.hdr', method='Imageio')
  834. self.displayFile('temp.hdr')
  835. def displaySplash(self):
  836. self.model.currentIMG = None
  837. self.displayFile('grey.hdr')
  838. def close(self):
  839. # check that no current display process already open
  840. if self.viewerProcess:
  841. # the display HDR process is already running
  842. # close current
  843. subprocess.run(['taskkill', '/F', '/T', '/IM', "HDRImageViewer*"],capture_output=False)
  844. self.viewerProcess = None
  845. # ------------------------------------------------------------------------------------------
  846. # ---- Class LchColorSelectorController ----------------------------------------------------
  847. # ------------------------------------------------------------------------------------------
  848. class LchColorSelectorController:
  849. def __init__(self, parent, idName = None):
  850. if pref.verbose: print(" [CONTROL] >> LchColorSelectorController.__init__(",") ")
  851. self.parent = parent
  852. self.model = model.LchColorSelectorModel(self)
  853. self.view = view.LchColorSelectorView(self)
  854. self.idName = idName
  855. self.callBackActive = True
  856. def sliderHueChange(self, vMin, vMax):
  857. values = self.model.setHueSelection(vMin,vMax)
  858. if self.callBackActive : self.parent.controller.changeColorEditor(values, self.idName)
  859. def sliderChromaChange(self, vMin, vMax):
  860. values = self.model.setChromaSelection(vMin,vMax)
  861. if self.callBackActive : self.parent.controller.changeColorEditor(values, self.idName)
  862. def sliderLightnessChange(self, vMin, vMax):
  863. values = self.model.setLightnessSelection(vMin,vMax)
  864. if self.callBackActive : self.parent.controller.changeColorEditor(values, self.idName)
  865. def sliderExposureChange(self, ev):
  866. values = self.model.setExposure(ev)
  867. if self.callBackActive : self.parent.controller.changeColorEditor(values, self.idName)
  868. def sliderSaturationChange(self, sat):
  869. values = self.model.setSaturation(sat)
  870. if self.callBackActive : self.parent.controller.changeColorEditor(values, self.idName)
  871. def sliderContrastChange(self, cc):
  872. values = self.model.setContrast(cc)
  873. if self.callBackActive : self.parent.controller.changeColorEditor(values, self.idName)
  874. def sliderHueShiftChange(self, hs):
  875. values = self.model.setHueShift(hs)
  876. if self.callBackActive : self.parent.controller.changeColorEditor(values, self.idName)
  877. def checkboxMaskChange(self,value):
  878. values = self.model.setMask(value)
  879. if self.callBackActive : self.parent.controller.changeColorEditor(values, self.idName)
  880. def setValues(self, values, callBackActive = False):
  881. if pref.verbose: print(" [CONTROL] >> LchColorSelectorController.setValue(",values,") ")
  882. self.callBackActive = callBackActive
  883. # slider hue selection
  884. v = values['selection']['hue'] if 'hue' in values['selection'].keys() else (0,360)
  885. self.view.sliderHueMin.setValue(int(v[0]))
  886. self.view.sliderHueMax.setValue(int(v[1]))
  887. # slider chroma selection
  888. v = values['selection']['chroma'] if 'chroma' in values['selection'].keys() else (0,100)
  889. self.view.sliderChromaMin.setValue(v[0])
  890. self.view.sliderChromaMax.setValue(v[1])
  891. # slider lightness
  892. v = values['selection']['lightness'] if 'lightness' in values['selection'].keys() else (0,100)
  893. self.view.sliderLightMin.setValue(int(v[0]*3))
  894. self.view.sliderLightMax.setValue(int(v[1]*3))
  895. # hue shift editor
  896. v = values['edit']['hue'] if 'hue' in values['edit'].keys() else 0
  897. self.view.sliderHueShift.setValue(int(v))
  898. self.view.valueHueShift.setText(str(v))
  899. # exposure editor
  900. v : int = values['edit']['exposure'] if 'exposure' in values['edit'].keys() else 0
  901. self.view.sliderExposure.setValue(int(v*30))
  902. self.view.valueExposure.setText(str(v))
  903. # contrast editor
  904. v : int = values['edit']['contrast'] if 'contrast' in values['edit'].keys() else 0
  905. self.view.sliderContrast.setValue(int(v))
  906. self.view.valueContrast.setText(str(v))
  907. # saturation editor
  908. v : int = values['edit']['saturation'] if 'saturation' in values['edit'].keys() else 0
  909. self.view.sliderSaturation.setValue(int(v))
  910. self.view.valueSaturation.setText(str(v))
  911. # mask
  912. v : bool = values['mask'] if 'mask' in values.keys() else False
  913. self.view.checkboxMask.setChecked(values['mask'])
  914. self.model.setValues(values)
  915. self.callBackActive = True
  916. # -----
  917. def resetSelection(self):
  918. if pref.verbose: print(" [CONTROL] >> LchColorSelectorController.resetSelection(",") ")
  919. default = copy.deepcopy(self.model.default)
  920. current = copy.deepcopy(self.model.getValues())
  921. current['selection'] = default['selection']
  922. self.setValues(current,callBackActive = True)
  923. self.callBackActive = True
  924. def resetEdit(self):
  925. if pref.verbose: print(" [CONTROL] >> LchColorSelectorController.resetEdit(",") ")
  926. default = copy.deepcopy(self.model.default)
  927. current = copy.deepcopy(self.model.getValues())
  928. current['edit'] = default['edit']
  929. self.setValues(current,callBackActive = True)
  930. self.callBackActive = True
  931. # ------------------------------------------------------------------------------------------
  932. # ---- Class LchColorSelectorController ----------------------------------------------------
  933. # ------------------------------------------------------------------------------------------
  934. class GeometryController:
  935. def __init__(self, parent ):
  936. if pref.verbose: print(" [CONTROL] >> GeometryController.__init__(",") ")
  937. self.parent = parent
  938. self.model = model.GeometryModel(self)
  939. self.view = view.GeometryView(self)
  940. self.callBackActive = True
  941. # callbacks
  942. def sliderCroppingVerticalAdjustementChange(self,v):
  943. values = self.model.setCroppingVerticalAdjustement(v)
  944. if self.callBackActive : self.parent.controller.changeGeometry(values)
  945. def sliderRotationChange(self,v):
  946. values = self.model.setRotation(v)
  947. if self.callBackActive : self.parent.controller.changeGeometry(values)
  948. def setValues(self, values, callBackActive = False):
  949. if pref.verbose: print(" [CONTROL] >> GeometryController.setValue(",values,") ")
  950. up = values['up'] if 'up' in values.keys() else 0.0
  951. rotation = values['rotation'] if 'rotation' in values.keys() else 0.0
  952. self.callBackActive = callBackActive
  953. self.view.sliderCroppingVerticalAdjustement.setValue(int(up))
  954. self.view.sliderRotation.setValue(int(rotation*6))
  955. self.model.setValues(values)
  956. self.callBackActive = True
  957. # ------------------------------------------------------------------------------------------
  958. # ---- Class AestheticsImageController -----------------------------------------------------
  959. # ------------------------------------------------------------------------------------------
  960. class ImageAestheticsController:
  961. def __init__(self, parent=None, HDRcontroller = None):
  962. if pref.verbose: print(" [CONTROL] >> AestheticsImageController.__init__(",")")
  963. self.parent = parent
  964. self.model = model.ImageAestheticsModel(self)
  965. self.view = view.ImageAestheticsView(self)
  966. # --------------------------------------------------------------------------------------
  967. def buildView(self,processPipe=None):
  968. if pref.verbose: print(" [CONTROL] >> AestheticsImageController.buildView()")
  969. # called when MultiDockController recall a controller/view
  970. self.view = view.ImageAestheticsView(self)
  971. if processPipe: self.setProcessPipe(processPipe)
  972. # --------------------------------------------------------------------------------------
  973. def setProcessPipe(self, processPipe):
  974. if pref.verbose: print(" [CONTROL] >> AestheticsImageController.setProcessPipe()")
  975. self.model.setProcessPipe(processPipe)
  976. self.view.setProcessPipe(processPipe, self.model.getPaletteImage())
  977. return True
  978. # ------------------------------------------------------------------------------------------
  979. # ------------------------------------------------------------------------------------------
  980. # --- message widget functions -------------------------------------------------------------
  981. # ------------------------------------------------------------------------------------------
  982. # ------------------------------------------------------------------------------------------
  983. def messageBox(title, text):
  984. msg = QMessageBox()
  985. msg.setText(text)
  986. msg.setStandardButtons(QMessageBox.Ok)
  987. msg.setWindowTitle(title)
  988. msg.setStandardButtons(QMessageBox.Ok)
  989. msg.setEscapeButton(QMessageBox.Close)
  990. msg.exec_()
  991. # -----------------------------------------------------------------------------
  992. def okCancelBox(title, text):
  993. msg = QMessageBox()
  994. msg.setText(text)
  995. msg.setWindowTitle(title)
  996. msg.setStandardButtons(QMessageBox.Ok | QMessageBox.Cancel)
  997. msg.setEscapeButton(QMessageBox.Close)
  998. return msg.exec_()
  999. # ------------------------------------------------------------------------------------------
  1000. # ---- Class ColorEditorsAutoController ----------------------------------------------------
  1001. # ------------------------------------------------------------------------------------------
  1002. class ColorEditorsAutoController:
  1003. def __init__(self, parent, controlledColorEditors, stepName ):
  1004. if pref.verbose: print(" [CONTROL] >> ColorEditorsAutoController.__init__(",") ")
  1005. self.parent = parent
  1006. self.controlled = controlledColorEditors
  1007. self.stepName =stepName
  1008. self.model = model.ColorEditorsAutoModel(self, stepName,len(controlledColorEditors), removeBlack= True)
  1009. self.view = view.ColorEditorsAutoView(self)
  1010. self.callBackActive = True
  1011. # callbacks
  1012. def auto(self):
  1013. if pref.verbose: print(" [CONTROL] >> ColorEditorsAutoController.auto(",") ")
  1014. for ce in self.controlled: ce.resetSelection(); ce.resetEdit()
  1015. values = self.model.compute()
  1016. if values != None:
  1017. for i,v in enumerate(values): self.controlled[i].setValues(v, callBackActive = False)