Batch Denoise UI
This is a tool that allows the user to select a starting frame and ending frame of Renderman rendered .exr files and run Renderman's denoiser in order to clean up the images.
​
This code consists of 2 parts. The first is the actual denoise operation, and the second script controls the interface and calls the denoise script to action.
​
The code to run the denoise script is as follows.
​
import os
import os.path
import sys
import re
import glob
import subprocess
class BruceOpps():
def __init__(self):
self.frames = ''
self.parent_dir = ''
self.exr_fullpath = ''
self.start_name = ''
self.start_num = ''
self.end_num = ''
self.bin_path = 'denoise' # assumes we are using linux
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):
# Now we know the start and end paths we should check if their exr names match
# - incase the user has not chosen the correct exr files.
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)
#print start_name
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)
#print self.exr_fullpath
self.start_name = start_name
self.start_num = start_num
self.end_num = end_num
#_________________________________________
def getListOfNumExts(self):
# Get a list of all the .exr files and then find the ones that
# are within the start_num and end_num range.
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
#_________________________________________
# The double underscore makes this a method that can only be used
# by instances of the class ie. it is a "private" procedure.
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__':
bruce = BruceOpps()
bruce.getListOfNumExts()
bruce.writeBatchScript()
bruce.runDenoiseCommand()
​
The second part of this script controls the user interface of the denoise tool. The reason that there are 2 scripts is because on controls the denoise operation and the other controls the UI and calls the denoise script into action.
​
from PyQt4.QtCore import *
from PyQt4.QtGui import *
from PyQt4 import uic
import os, re, sys, subprocess
import bruce_oops
reload(bruce_oops)
class DenoiseWindow(QMainWindow):
def __init__(self):
super(DenoiseWindow, self).__init__()
uic.loadUi(os.path.join(os.path.dirname(__file__), 'UI', 'denoise.ui'), self)
self.output = []
self.makeConnections()
self.bruce = bruce_oops.BruceOpps()
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_firstFrame.setText(self.pathToFarm)
self.LNE_endFrame.setText(self.pathToFarm)
rman_dir = os.environ['RMANTREE']
self.denoiser = os.path.join(rman_dir, 'bin', 'denoise')
self.LNE_osBrowse.setText(self.denoiser)
def makeConnections(self):
self.B_firstFrame.clicked.connect(self.browseStart)
self.B_endFrame.clicked.connect(self.browseEnd)
self.B_osBrowse.clicked.connect(self.browseBin)
self.B_Denoise.clicked.connect(self.execute)
self.B_Cancel.clicked.connect(sys.exit)
def browseStart(self):
path = QFileDialog.getOpenFileName(self, caption='Choose start frame', directory=os.path.expanduser('~'))
self.LNE_firstFrame.setText(path)
def browseEnd(self):
path = QFileDialog.getOpenFileName(self, caption='Choose end frame', directory=os.path.expanduser('~'))
self.LNE_endFrame.setText(path)
def browseBin(self):
path = QFileDialog.getOpenFileName(self, caption='Choose denoise bin script', directory=os.path.expanduser('~'))
self.LNE_osBrowse.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.bruce.setBinPath( str(self.LNE_osBrowse.text()) )
print self.LNE_firstFrame.text()
self.bruce.setStartEndPaths(str(self.LNE_firstFrame.text()), str(self.LNE_endFrame.text()))
self.bruce.getListOfNumExts()
self.bruce.writeBatchScript()
cmd = self.bruce.command
process = subprocess.Popen(cmd, shell=True, stderr=subprocess.STDOUT, stdout=subprocess.PIPE)
for line in iter(process.stdout.readline, ''):
#sys.stdout.write(line)
self.updateOutput(line)
if __name__ == '__main__':
app = QApplication(sys.argv)
window = DenoiseWindow()
window.show()
sys.exit(app.exec_())

