123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450 |
- # import
- # ------------------------------------------------------------------------------------------
- import os, sys, math, json
- import matplotlib.pyplot as plt
- import numpy as np
- import easygui
- # miam import
- from . import WFNode, WFProcess, WFConnector
- import miam.image.Image as MIMG
- import miam.histogram.Histogram as MHIST
- import miam.image.channel
- import miam.utils
- from miam.processing import (ColorSpaceTransform, ContrastControl, Duplicate, ExposureControl, NoOp,
- TMO_CCTF, TMO_Lightness, TMO_Linear, Ymap, Blend, MaskSegmentPercentile,
- Fuse
- )
- # ------------------------------------------------------------------------------------------
- # MIAM project 2020
- # ------------------------------------------------------------------------------------------
- # author: remi.cozot@univ-littoral.fr
- # ------------------------------------------------------------------------------------------
- class WFWorkflow(object):
- """container of workflow"""
- def __init__(self, name='workflow:noname'):
- # attributes
- self.name = name
- # list of processes
- self.processes = []
- # list of images
- self.connectors = []
- # root
- self.root = None
- # end
- self.leafs = []
- # methods
- # ---------------------------------------------
- # add process, connect, root and leaf
- # ---------------------------------------------
- def addProcess(self,p):
- self.processes.append(p)
- return p
- # connecting processes
- def connect(self, outputProcess=None, inputProcess=None, name=None):
- if (not outputProcess) or (not inputProcess):
- if not outputProcess: print("[ERROR] WFWorkflow.connect(): unknown output !")
- if not inputProcess: print("[ERROR] WFWorkflow.connect(): unknown input !")
- return None
- else:
- # outputProcess -- WFImage --> inputProcess
- outputProcess = self.getByName(outputProcess) if isinstance(outputProcess,str) else outputProcess
- inputProcess = self.getByName(inputProcess) if isinstance(inputProcess,str) else inputProcess
- if not outputProcess: print("[ERROR] WFWorkflow.connect(): unknown output !")
- if not inputProcess: print("[ERROR] WFWorkflow.connect(): unknown input !")
- if not name:
- name = name=outputProcess.name+"->"+inputProcess.name
- connector = WFConnector.WFConnector(name=name)
- # link components
- outputProcess.outputs.append(connector)
- inputProcess.inputs.append(connector)
- connector.outputOf = outputProcess
- connector.inputOf = inputProcess
- self.connectors.append(connector)
- return connector
- def setRoot(self,p):
- p = self.getByName(p) if isinstance(p,str) else p
- if not p: print("[ERROR] WFWorkflow.setRoot(): unknown process !")
- connector = WFConnector.WFConnector(name="root"+"->"+p.name)
- connector.isRoot = True
- self.connectors.append(connector)
- self.root = connector
- # link components
- p.inputs.append(connector)
- connector.inputOf = p
- return connector
- def setLeaf(self,p):
- p = self.getByName(p) if isinstance(p,str) else p
- if not p: print("[ERROR] WFWorkflow.setLeaf(): unknown process !")
- connector = WFConnector.WFConnector(name=p.name+"->"+"leaf")
- connector.isLeaf = True
- self.connectors.append(connector)
- self.leafs.append(connector)
- # link components
- p.outputs.append(connector)
- connector.outputOf = p
- return connector
- # ---------------------------------------------
- # utils method
- # ---------------------------------------------
- def checkName(self, name):
- res = []
- for p in self.processes:
- if p.name == name: res.append(p)
- for i in self.connectors:
- if i.name == name: res.append(i)
- return res
- def getByName(self, name):
- res = None
- asName = self.checkName(name)
- if len(asName)>0: res = asName[0]
- return res
- # ---------------------------------------------
- # computing
- # ---------------------------------------------
- def compute(self, input=None):
- #
- #
- if not input:
- filename = easygui.fileopenbox(msg="select image.")
- print("selected image:", filename)
- # reading image
- input= MIMG.Image.readImage(filename)
- if input.isHDR(): input = input.removeZeros(0.5)
- self.root.image=input
- # main loop:
- # -------------------------------------------------
- awaitingProcess = []
- # reset ready for compute
- for con in self.connectors:
- con.ready = False
- # start with root
- self.root.ready = True
- # put all processes in awaiting Process
- awaitingProcess = self.processes
- # -------------------------------------------------
- while len(awaitingProcess)>0 :
- # ---------------------------------------------
- # looking for first process in list that all inputs are ready
- pReady = None
- pNotReady = []
- for p in awaitingProcess:
- if p.isReady() and (not pReady):
- # take first ready for computation
- pReady = p
- else: # not ready or firest ready already find
- pNotReady.append(p)
- awaitingProcess = pNotReady
- pReady.compute()
-
- def compile(self):
- # check process input and output
- print("+-----------------------------------")
- print("| COMPILE WORKFLOW")
- print("+-----------------------------------")
- print("| check process input and output")
- print("+-----------------------------------")
- noInput =[]
- noOutput =[]
- for p in self.processes:
- print("| process:",p.name)
- print("|-----------------------------------")
- print("| input(s) -> ")
- if len(p.inputs) == 0:
- # no input
- noInput.append(p)
- print("| ", "no input -> root")
- for input in p.inputs:
- print("| ",input.name)
- print("| output(s) -> ")
- if len(p.outputs) == 0:
- # no output
- noOutput.append(p)
- print("| ", "no output -> leaf")
-
- for output in p.outputs:
- print("| ",output.name)
- print("+-----------------------------------")
- # root
- if self.root and (len(noInput)== 0):
- print("| root:",self.root.name, ": OK")
- print("+-----------------------------------")
- if (not self.root) and (len(noInput)== 1):
- # add Root
- self.setRoot(noInput[0])
- print("| root -> pocess:",noInput[0].name)
- print("| root:",self.root.name, ": OK")
- print("+-----------------------------------")
- noInput.pop()
- if not self.root or (len(noInput)>1):
- print("[ERROR] root error !")
- # leaf
- for p in noOutput:
- self.setLeaf(p)
- print("| process:",p.name, "- > leaf")
- if len(self.leafs)>0:
- for c in self.leafs:
- print("+-----------------------------------")
- print("| leaf:",c.name)
- print("| leaf: OK")
- print("+-----------------------------------")
- else:
- print("[ERROR] leaf error !")
- # simulate processing
- # -------------------------------------------------
- print("| check workflow")
- print("+-----------------------------------")
- awaitingProcess = []
- # reset ready for compute
- for con in self.connectors:
- con.ready = False
- # start with root
- self.root.ready = True
- # put all processes in awaiting Process
- awaitingProcess = self.processes
- # -------------------------------------------------
- while len(awaitingProcess)>0 :
- # ---------------------------------------------
- # looking for first process in list that all inputs are ready
- pReady = None
- pNotReady = []
- for p in awaitingProcess:
- if p.isReady() and (not pReady):
- # take first ready for computation
- pReady = p
- else: # not ready or firest ready already find
- pNotReady.append(p)
- awaitingProcess = pNotReady
- # 'compute'
- print("+-----------------------------------")
- print("| computing:", pReady.name)
- # set ouputs to ready
- for con in pReady.outputs:
- con.ready = True
- print("| +---->", con.name, "ready")
- print("+-----------------------------------")
- print("| check workflow: end reached: OK")
- print("+-----------------------------------")
- return self
- # ---------------------------------------------
- # read
- # ---------------------------------------------
- def readWorkflow(filename):
- # read filename
- with open(filename) as json_file:
- jsonData = json.load(json_file)
- # recover data
- # name
- wf_name = jsonData['name'] if 'name' in jsonData else filename
- # processes and connectors
- processesJSON = jsonData['processes'] if 'processes' in jsonData else []
- connectorsJSON = jsonData['connectors'] if 'connectors' in jsonData else []
- # build process and connectors
- error = False
- # create wf
- wf = WFWorkflow(name=wf_name)
- # build process
- for processJSON in processesJSON:
- # type, name, params
- if 'type' in processJSON :
- type= processJSON['type']
- else:
- print("ERROR[miam.workflow.WFWorflow.readWorkflow(",processJSON,"): no type found !]")
- error = True
- if 'name' in processJSON:
- process_name= processJSON['name']
- else:
- print("ERROR[miam.workflow.WFWorflow.readWorkflow(",processJSON,"): no name found !]")
- error = True
- if 'parameters' in processJSON:
- parameters= processJSON['parameters']
- else:
- print("ERROR[miam.workflow.WFWorflow.readWorkflow(",processJSON,"): no parameters found !]")
- error = True
- # build process
- if not error:
- # add here all new processing class
- # --------------------------------------------
- # ColorSpaceTransform
- # ContrastControl
- # Duplicate
- # ExposureControl
- # NoOp
- # TMO_CCTF
- # TMO_Lightness
- # TMO_Linear
- # Ymap
- # Blend
- # MaskSegmentPercentile
- # Fuse
- # --------------------------------------------
- if type =='ColorSpaceTransform':
- wf.addProcess(WFProcess.WFProcess(
- name = process_name,
- process=ColorSpaceTransform.ColorSpaceTransform()).setParameters(eval(parameters))
- )
- elif type == 'ContrastControl':
- wf.addProcess(WFProcess.WFProcess(
- name = process_name,
- process=ContrastControl.ContrastControl()).setParameters(eval(parameters))
- )
- elif type == 'Duplicate':
- wf.addProcess(WFProcess.WFProcess(
- name = process_name,
- process=Duplicate.Duplicate()).setParameters(eval(parameters))
- )
- elif type == 'ExposureControl':
- wf.addProcess(WFProcess.WFProcess(
- name = process_name,
- process=ExposureControl.ExposureControl()).setParameters(eval(parameters))
- )
- elif type == 'NoOp':
- wf.addProcess(WFProcess.WFProcess(
- name = process_name,
- process=NoOp.NoOp())
- )
- elif type == 'TMO_CCTF':
- wf.addProcess(WFProcess.WFProcess(
- name = process_name,
- process=TMO_CCTF.TMO_CCTF()).setParameters(eval(parameters))
- )
- elif type == 'TMO_Lightness':
- wf.addProcess(WFProcess.WFProcess(
- name = process_name,
- process=TMO_Lightness.TMO_Lightness()).setParameters(eval(parameters))
- )
- elif type == 'TMO_Linear':
- wf.addProcess(WFProcess.WFProcess(
- name = process_name,
- process=TMO_Linear.TMO_Linear()).setParameters(eval(parameters))
- )
- elif type == 'Ymap':
- wf.addProcess(WFProcess.WFProcess(
- name = process_name,
- process=Ymap.Ymap()).setParameters(eval(parameters))
- )
- elif type == 'Blend':
- wf.addProcess(WFProcess.WFProcess(
- name = process_name,
- process=Blend.Blend()).setParameters(eval(parameters))
- )
- elif type == 'MaskSegmentPercentile':
- wf.addProcess(WFProcess.WFProcess(
- name = process_name,
- process=MaskSegmentPercentile.MaskSegmentPercentile()).setParameters(eval(parameters))
- )
- elif type == 'Fuse':
- wf.addProcess(WFProcess.WFProcess(
- name = process_name,
- process=Fuse.Fuse()).setParameters(eval(parameters))
- )
- else:
- print("ERROR[miam.workflow.WFWorflow.readWorkflow(",filename,"): unkown process type: ",type," !]")
- error = True
- # build connector
- for connectorJSON in connectorsJSON:
- # outputProcess -> inputProcess
- if 'outputProcess' in connectorJSON :
- outputProcess = connectorJSON['outputProcess']
- else:
- print("ERROR[miam.workflow.WFWorflow.readWorkflow(",connectorJSON,"): no outputProcess found !]")
- error = True
- if 'inputProcess' in connectorJSON:
- inputProcess= connectorJSON['inputProcess']
- else:
- print("ERROR[miam.workflow.WFWorflow.readWorkflow(",connectorJSON,"): no inputProcess found !]")
- error = True
- # find output and input
- pout = wf.getByName(outputProcess)
- pin = wf.getByName(inputProcess)
- wf.connect(outputProcess=pout, inputProcess=pin, name=outputProcess+"->"+inputProcess)
- # compile and return
- return wf.compile()
- # ---------------------------------------------
- # display
- # ---------------------------------------------
- def display(self, withHistogram=True):
- # number of leaf image
- nbRes = len(self.leafs)
- # dual display
- dd = 2 if withHistogram else 1
- fig, ax = plt.subplots(2,nbRes*dd)
- # display input
- self.root.image.plot(ax[0,0])
- if withHistogram:
- if self.root.image.isHDR():
- ch = miam.image.channel.channel.Y
- else:
- ch = miam.image.channel.channel.L
- MHIST.Histogram.build(self.root.image, ch).plot(ax[0,1])
-
- # display results
- for i, leaf in enumerate(self.leafs):
- leaf.image.plot(ax[1,i*dd])
- if withHistogram:
- if leaf.image.isHDR():
- ch = miam.image.channel.channel.Y
- else:
- ch = miam.image.channel.channel.L
- MHIST.Histogram.build(leaf.image, ch).plot(ax[1,i*dd+1])
- plt.show(block=True)
|