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_())

