Batch Denoise GUI
This page explains the development of a GUI that enables a non-programmer to batch denoise a sequence of RenderMan .exr image files.
ALGORITHM
1. Create a UI template in QT Designer.
2. Create a class from the batch denoise script.
2.1. Set the frame range and checks for errors.
2.2. Set the bin path.
2.3. Get list of frame numbers.
2.4. Write out a batch render file.
3. Connect the controls for the GUI.
4. Run the batch denoise from the GUI. See instructions below.
INSTRUCTIONS
To Run the Script:
Create a directory for the Denoise GUI.
Place the "denoise_oops.py" and "denoiseUI.py" to the directory.
Create a subdirectory called "ui".
Place the "denoiserWindow.ui" into this subdirectory.
On Linux double click the file called "denoiseUI", on Windows double click the file called "denoiseUI.bat".
To Use the Deniose GUI:
Click the "Browse" button next to the "start frame file path" bar and find the first image in the sequence you wish to denoise.
Click the "Browse" button next to the "end frame file path" bar and find the last image in the sequence you wish to denoise.
Click the "Browse" button next to the "denoise bin path" bar and select the directory you wish to set as the bin path for the images.
Click the "Do It" button and the progress of the denoising process will be printed in the Output box.
Click the "Cancel" button to close the GUI.
THE CODE - "denoise_oops.py"
import os import os.path import re import glob import subprocess class DenoiseOops(): def __init__(self): self.frames = '' self.parent_dir = '' self.exr_fullpath = '' self.start_name = '' self.start_num = '' self.end_num = '' self.bin_path = 'denoise' self.flags = ' --crossframe -v variance -f default.filter.json ' self.batch_script = '' def setBinPath(self, path): self.bin_path = path def setStartEndPaths(self, start_exr_path, end_exr_path): # Check to see if the .exr names match. start_dir, start_name, start_num = self.get_dir_name_num(start_exr_path) end_dir, end_name, end_num = self.get_dir_name_num(end_exr_path) if not start_name.endswith('variance'): raise RuntimeError("Only variance files can be denoised") if not start_name == end_name: raise RuntimeError("OpenEXR names don't match :(") if not start_dir == end_dir: raise RuntimeError("OpenEXR directories don't match :(") self.parent_dir = start_dir self.exr_fullpath = os.path.join(start_dir, start_name) self.start_name = start_name self.start_num = start_num self.end_num = end_num # Obtain a list of all .exr files and find ones within the start_num and end_num range. def getListOfNumExts(self): glob_pattern = os.path.join(self.parent_dir, '*_variance.*.exr') paths = glob.glob(glob_pattern) listOfNumExt = [] for path in paths: dir,name,num = self.get_dir_name_num(path) if name == self.start_name and int(num) >= int(self.start_num) and int(num) <= int(self.end_num): listOfNumExt.append(num) self.frames = (",".join(listOfNumExt)) def writeBatchScript(self): self.command = self.bin_path + self.flags + self.exr_fullpath + '.{' + self.frames + '}.exr' if os.name == 'nt': batch_name = self.start_name + '.bat' else: batch_name = self.start_name self.batch_script = os.path.join(self.parent_dir, batch_name) batch = open(self.batch_script , 'w') batch.write(self.command) batch.close() def runDenoiseCommand(self): process = subprocess.Popen(self.command, shell=True, stderr=subprocess.STDOUT, stdout=subprocess.PIPE) for line in iter(process.stdout.readline, ''): print line def get_dir_name_num(self, path): print path parent_dir = os.path.dirname(path) name = os.path.basename(path) reg_pat = re.compile(r'(\w+)[._]+(\d+)[._]+exr') found_it = re.search(reg_pat, name) if found_it: name = found_it.group(1) num = found_it.group(2) return [parent_dir, name, num] else: return None if __name__ == '__main__': denoise = DenoiseOops() denoise.setStartEndPaths('.../images/demo__perspShape_beauty_variance.0003.exr', '.../images/demo__perspShape_beauty_variance.0007.exr') denoise.getListOfNumExts() denoise.writeBatchScript() denoise.runDenoiseCommand()
THE CODE - "denoiseUI.py"
from PyQt4.QtCore import * from PyQt4.QtGui import * from PyQt4 import uic import os import re import sys import subprocess import denoise_oops reload(denoise_oops) class DenoiseWindow(QMainWindow): def __init__(self): super(DenoiseWindow, self).__init__() uic.loadUi(os.path.join(os.path.dirname(__file__), 'ui', 'denoiserWindow.ui'), self) self.output = [] self.makeConnections() self.denoise = denoise_oops.DenoiseOops() # Set a reasonable path to the where the images for denoising are most likely to be stored. home_dir = os.environ['HOME'] if os.name == 'posix': self.pathToFarm = os.path.join(home_dir, 'mount', 'renderfarm') else: self.pathToFarm = 'I:\Savannah\SDM Render Farm' self.LNE_startFrame.setText(self.pathToFarm) self.LNE_endFrame.setText(self.pathToFarm) # Set a full path to the denoise application. rman_dir = os.environ['RMANTREE'] self.denoiser = os.path.join(rman_dir, 'bin', 'denoise') self.LNE_bin.setText(self.denoiser) # Make connections. def makeConnections(self): self.BTN_startBrowse.clicked.connect(self.browseStart) self.BTN_endBrowse.clicked.connect(self.browseEnd) self.BTN_binBrowse.clicked.connect(self.browseBin) self.BTN_execute.clicked.connect(self.execute) self.BTN_cancel.clicked.connect(sys.exit) def browseStart(self): path = QFileDialog.getOpenFileName(self, caption='Choose start frame', directory=self.pathToFarm) self.LNE_startFrame.setText(path) def browseEnd(self): path = QFileDialog.getOpenFileName(self, caption='Choose end frame', directory=self.pathToFarm) self.LNE_endFrame.setText(path) def browseBin(self): path = QFileDialog.getOpenFileName(self, caption='denoise bin directory', directory=os.path.expanduser('~')) self.LNE_bin.setText(path) def updateOutput(self, line): self.output.append(line) self.TXT_output.setPlainText('\n'.join(self.output)) self.TXT_output.verticalScrollBar().setValue(self.TXT_output.verticalScrollBar().maximum()) def execute(self): self.denoise.setBinPath( str(self.LNE_bin.text()) ) print self.LNE_startFrame.text() self.denoise.setStartEndPaths(str(self.LNE_startFrame.text()), str(self.LNE_endFrame.text())) self.denoise.getListOfNumExts() self.denoise.writeBatchScript() cmd = self.denoise.command process = subprocess.Popen(cmd, shell=True, stderr=subprocess.STDOUT, stdout=subprocess.PIPE) for line in iter(process.stdout.readline, ''): self.updateOutput(line)
SHELL SCRIPTS
Linux - "denoiseUI"
python denoiseUI.py
read
Windows - "denoiseUI.bat"
C:\python27\python denoiseUI.py
pause
Denoiser GUI Template in Qt Designer
Denoiser GUI
RESTRICTIONS
This is meant as an example for the process of denoising a batch of rendered .exr files. PyQt4 needs to be installed on the respective computers for the code to work properly. The initial directory paths are predetermined by the code. They can be changed by pressing the "Browse" buttons, but if the user desires to set other initial directories upon opening the window, he or she must manually change the code. This code should be use as a starting point and should be tailored to suit the user's needs.