Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Nuke node to automate the blending of multiple bracketed exposures into a single high dynamic range hdr image. Includes a script to sort the input nodes based on exposure time.

Exposure Bracket Blender

ExposureBracketBlender is a Nuke node to automate the blending of multiple bracketed exposures into a single high dynamic range hdr image.

If you have many source images that you need to blend and stitch into latlong images, it includes a script to sort the input nodes based on exposure time. You can have hundreds of source images and it sorts them and creates groups that each have exposure blend tools connected.

There are two copies of the node included. One has the python self-contained in the python buttons on the node. If you prefer, you can also take the other one which references the bracketblender python module, so you don't copy and paste the code everywhere and slow down your script.

Full credit goes to Santi Svirsky for the technique.

Have fun!

import nuke
import os, math
from datetime import datetime
def hex_color(r, g=None, b=None):
# Return hex code for given rgb tuple. g and b are optional. If not specified, a greyscale value will be returned.
# r g and b should be between 0 and 1
if not g:
g = r
if not b:
b = r
return int('%02x%02x%02x%02x' % (r*255,g*255,b*255,1), 16)
def unselect():
_ = [n.setSelected(False) for n in nuke.allNodes(recurseGroups=True)]
def sort():
# For selected read nodes:
# Sort by exposure time, seperate by bracked set
grid_x = int(nuke.toNode('preferences').knob('GridWidth').value())
grid_y = int(nuke.toNode('preferences').knob('GridHeight').value())
node = nuke.thisNode()
nuke.root().begin()
nodes = nuke.selectedNodes('Read')
if not nodes:
nuke.message("Please select a number of read nodes to sort.")
return
unselect()
node.setSelected(True)
nuke.nodeCopy('%clipboard')
unselect()
task = nuke.ProgressTask('Sorting {0} Exposure Brackets'.format(len(nodes)))
# http://strftime.org
data = {n:(n.metadata().get('input/exposure_time'), datetime.strptime(n.metadata().get('exr/Exif:DateTimeOriginal'), r'%Y:%m:%d %H:%M:%S')) for n in nodes}
# bracket_sets is a list that will contain lists of exposure bracket sets
bracket_sets = []
bracket = []
# Loop through all images, group into exposure brackets by calculating time difference
data_by_time = sorted(data.items(), key=lambda x: x[1][1])
for i, exposure in enumerate(data_by_time):
read = exposure[0]
etime = exposure[1][0]
ctime = exposure[1][1]
task.setProgress( int( float(i) / len(data_by_time) * 100) )
if task.isCancelled():
break
task.setMessage('Getting next exposure time: \t{0}'.format(read.name()))
if i < len(data_by_time)-1:
next_ctime = data_by_time[i+1][1][1]
task.setMessage('Calc time diff: \t{0}'.format(read.name()))
time_diff = next_ctime - ctime
task.setMessage('Printing info: \t{0}'.format(read.name()))
# print "===> ", read.name()
# print etime
# print ctime.isoformat()
# print time_diff.total_seconds()
# print
task.setMessage('Append lists: \t{0}'.format(read.name()))
if time_diff.total_seconds() > 2.0:
# print "New bracket Detected!\n------------------------------------------------------------\n\n"
bracket.append((read, etime, ctime, time_diff.total_seconds()))
bracket_sets.append(bracket)
bracket = []
else:
bracket.append((read, etime, ctime, time_diff.total_seconds()))
# Add last images to the last exposure set
if i == len(data_by_time)-1:
bracket_sets.append(bracket)
bracket = []
pos = (node.xpos(), node.ypos() - grid_y*12)
# Loop through each exposure set, sort by exposure time dark to bright
# Sort horizontally
for i, eset in enumerate(bracket_sets):
if task.isCancelled():
break
esetsort = sorted(eset, key=lambda x: x[1])
set_size = len(esetsort)
print "Exposure set {0} contains {1} images: \n\t{2}".format(i, set_size, "\n\t".join(["{0} \t\t- {1} \t: {2} \t- {3}".format(e[0].name(), os.path.basename(e[0]['file'].getValue()), e[1], e[2]) for e in esetsort]))
task.setMessage('Sorting exposure set: \t{0}'.format(i))
# Create new exposure blender if we have a complete exposure set
if set_size > 1:
nuke.nodePaste("%clipboard")
new_brack = nuke.selectedNode()
unselect()
new_brack.setXYpos(pos[0], pos[1]+grid_y*10)
new_brack['exposures'].setValue(set_size)
# Set base exposure to 1 up from middle frame
# new_brack['base_exposure'].setValue(esetsort[-2][1])
new_brack['base_exposure'].setValue(esetsort[int(math.ceil(float(set_size)/2))][1])
for j, e in enumerate(esetsort):
if task.isCancelled():
break
cur_node = e[0]
etime = e[1]
ctime = e[2]
cur_node.setXYpos(pos[0], pos[1])
cur_node['tile_color'].setValue(hex_color(float(j)/set_size*0.8+0.2))
if set_size > 1:
new_brack.connectInput(0, cur_node)
pos = (pos[0] + grid_x, pos[1])
# space between big exposure time diffs
# if e[3] > 100:
# pos = (pos[0] + grid_x*4, pos[1])
pos = (pos[0] + grid_x, pos[1])
nuke.delete(node)
def expand_group():
node = nuke.thisNode()
nuke.root().begin()
_ = [n.setSelected(False) for n in nuke.allNodes(recurseGroups=True)]
node.setSelected(True)
# reverses the order of the inputs...
#nuke.expandSelectedGroup()
node.begin()
_ = [n.setSelected(True) for n in nuke.allNodes()]
nuke.nodeCopy('%clipboard')
nuke.root().begin()
_ = [n.setSelected(False) for n in nuke.allNodes(recurseGroups=True)]
nuke.nodePaste('%clipboard')
copied_nodes = nuke.selectedNodes()
_ = [n.setSelected(False) for n in nuke.allNodes(recurseGroups=True)]
print [n.name() for n in copied_nodes]
input0 = nuke.toNode('Input0')
offset = (input0.xpos() - node.xpos(), input0.ypos() - node.ypos())
print offset
_ = [n.setXYpos(n.xpos() - offset[0], n.ypos() - offset[1]) for n in copied_nodes]
for i in xrange(node.inputs()):
inputNode = nuke.toNode('Input{0}'.format(i))
inputNode.setInput(0, node.input(i))
_ = [nuke.delete(n) for n in copied_nodes if n.Class() in ['Input', 'Output']]
set cut_paste_input [stack 0]
BackdropNode {
inputs 0
name BackdropNode4
tile_color 0x7f7f7f01
label "Self contained python"
note_font_size 25
selected true
xpos -1456
ypos 883
bdwidth 272
bdheight 249
}
BackdropNode {
inputs 0
name BackdropNode5
tile_color 0x7f7f7f01
label "References External\npython module"
note_font_size 25
selected true
xpos -1786
ypos 883
bdwidth 272
bdheight 249
}
push $cut_paste_input
Group {
name ExposureBracketBlender6
label "\[value exposures] Exposures"
selected true
xpos -1690
ypos 992
addUserKnob {20 Blend}
addUserKnob {22 sort_selected_reads l "Sort Selected Reads" t "Sort selected read nodes by exposure time, and create an ExposureBracketBlender for each exposure set.\n\n" T "import bracketblender\nbracketblender.sort()" +STARTLINE}
addUserKnob {22 ungroup l Ungroup t "Ungroup this ExposureBracketBlender so you can manipulate the graph more easily.\n\nUseful if you need to keep only pieces of certain exposures, for example if there is motion in the frame." -STARTLINE T "import bracketblender\nbracketblender.expand_group()"}
addUserKnob {26 ""}
addUserKnob {41 exposures T CTRL.exposures}
addUserKnob {41 base_exposure l base_exp T CTRL.base_exposure}
addUserKnob {41 src l grey t "Sample diffuse grey to expose your image (for example the 18% grey patch on a macbeth chart)." T diffuse_grey.src}
addUserKnob {26 label_keyer l " "}
addUserKnob {41 highlight_clip T CTRL.highlight_clip}
addUserKnob {41 shad_clip T CTRL.shad_clip}
addUserKnob {26 _1 l " "}
addUserKnob {41 shadow_bias T CTRL.shadow_bias}
addUserKnob {41 key_falloff T CTRL.key_falloff}
addUserKnob {41 key_bias T CTRL.key_bias}
addUserKnob {41 alpha_blur T CTRL.alpha_blur}
addUserKnob {20 keyer_ranges l "keyer ranges" n 1}
keyer_ranges 0
addUserKnob {41 i0 T CTRL.i0}
addUserKnob {41 i1 T CTRL.i1}
addUserKnob {41 i2 T CTRL.i2}
addUserKnob {41 i3 T CTRL.i3}
addUserKnob {41 i4 T CTRL.i4}
addUserKnob {41 i5 T CTRL.i5}
addUserKnob {41 i6 T CTRL.i6}
addUserKnob {20 endGroup n -1}
}
Input {
inputs 0
name Input6
xpos 620
ypos 206
number 6
}
Keyer {
operation "luminance key"
range {{"end ? 0 : min(B, parent.CTRL.shad_clip)"} {"end ? 0 : max(parent.CTRL.shad_clip, falloff*bias * (-parent.CTRL.shadow_bias+1))"} {"start ? 1 : min((1 - (falloff*(1-bias))), (1-parent.CTRL.highlight_clip))"} {"start ? 1 : max(C, 1-parent.CTRL.highlight_clip)"}}
name Keyer6
xpos 620
ypos 296
addUserKnob {20 User}
addUserKnob {3 number}
number 6
addUserKnob {7 falloff}
falloff {{parent.CTRL.key_falloff}}
addUserKnob {7 bias}
bias {{"start ? 1 : end ? 0 : (1-log_exp)*parent.CTRL.key_bias"}}
addUserKnob {7 log_exp}
log_exp {{"(1 - log(exposure)/7)/2"}}
addUserKnob {7 exposure}
exposure {{"base_exposure / \[metadata input/exposure_time]"}}
addUserKnob {7 base_exposure}
base_exposure {{parent.CTRL.base_exposure}}
addUserKnob {6 start +STARTLINE}
start {{"number == 0"}}
addUserKnob {6 end +STARTLINE}
end {{"number == (parent.CTRL.exposures-1)"}}
}
Multiply {
channels rgb
value {{"base_exp / \[metadata input/exposure_time]"}}
name Multiply6
label "\[value value]"
xpos 620
ypos 368
addUserKnob {20 BaseExp}
addUserKnob {7 base_exp}
base_exp {{parent.CTRL.base_exposure}}
}
Blur {
channels alpha
size {{parent.CTRL.alpha_blur}}
name Blur2
xpos 620
ypos 440
}
Premult {
name Premult6
xpos 620
ypos 518
}
Input {
inputs 0
name Input5
xpos 510
ypos 206
number 5
}
Keyer {
operation "luminance key"
range {{"end ? 0 : min(B, parent.CTRL.shad_clip)"} {"end ? 0 : max(parent.CTRL.shad_clip, falloff*bias * (-parent.CTRL.shadow_bias+1))"} {"start ? 1 : min((1 - (falloff*(1-bias))), (1-parent.CTRL.highlight_clip))"} {"start ? 1 : max(C, 1-parent.CTRL.highlight_clip)"}}
name Keyer5
xpos 510
ypos 296
addUserKnob {20 User}
addUserKnob {3 number}
number 5
addUserKnob {7 falloff}
falloff {{parent.CTRL.key_falloff}}
addUserKnob {7 bias}
bias {{"start ? 1 : end ? 0 : (1-log_exp)*parent.CTRL.key_bias"}}
addUserKnob {7 log_exp}
log_exp {{"(1 - log(exposure)/7)/2"}}
addUserKnob {7 exposure}
exposure {{"base_exposure / \[metadata input/exposure_time]"}}
addUserKnob {7 base_exposure}
base_exposure {{parent.CTRL.base_exposure}}
addUserKnob {6 start +STARTLINE}
start {{"number == 0"}}
addUserKnob {6 end +STARTLINE}
end {{"number == (parent.CTRL.exposures-1)"}}
}
Multiply {
channels rgb
value {{"base_exp / \[metadata input/exposure_time]"}}
name Multiply1
label "\[value value]"
xpos 510
ypos 368
addUserKnob {20 BaseExp}
addUserKnob {7 base_exp}
base_exp {{parent.CTRL.base_exposure}}
}
Blur {
channels alpha
size {{parent.CTRL.alpha_blur}}
name Blur3
xpos 510
ypos 440
}
Premult {
name Premult1
xpos 510
ypos 518
}
Input {
inputs 0
name Input4
xpos 400
ypos 206
number 4
}
Keyer {
operation "luminance key"
range {{"end ? 0 : min(B, parent.CTRL.shad_clip)"} {"end ? 0 : max(parent.CTRL.shad_clip, falloff*bias * (-parent.CTRL.shadow_bias+1))"} {"start ? 1 : min((1 - (falloff*(1-bias))), (1-parent.CTRL.highlight_clip))"} {"start ? 1 : max(C, 1-parent.CTRL.highlight_clip)"}}
name Keyer4
xpos 400
ypos 296
addUserKnob {20 User}
addUserKnob {3 number}
number 4
addUserKnob {7 falloff}
falloff {{parent.CTRL.key_falloff}}
addUserKnob {7 bias}
bias {{"start ? 1 : end ? 0 : (1-log_exp)*parent.CTRL.key_bias"}}
addUserKnob {7 log_exp}
log_exp {{"(1 - log(exposure)/7)/2"}}
addUserKnob {7 exposure}
exposure {{"base_exposure / \[metadata input/exposure_time]"}}
addUserKnob {7 base_exposure}
base_exposure {{parent.CTRL.base_exposure}}
addUserKnob {6 start +STARTLINE}
start {{"number == 0"}}
addUserKnob {6 end +STARTLINE}
end {{"number == (parent.CTRL.exposures-1)"}}
}
Multiply {
channels rgb
value {{"base_exp / \[metadata input/exposure_time]"}}
name Multiply2
label "\[value value]"
xpos 400
ypos 368
addUserKnob {20 BaseExp}
addUserKnob {7 base_exp}
base_exp {{parent.CTRL.base_exposure}}
}
Blur {
channels alpha
size {{parent.CTRL.alpha_blur}}
name Blur4
xpos 400
ypos 440
}
Premult {
name Premult2
xpos 400
ypos 518
}
Input {
inputs 0
name Input3
xpos 290
ypos 206
number 3
}
Keyer {
operation "luminance key"
range {{"end ? 0 : min(B, parent.CTRL.shad_clip)"} {"end ? 0 : max(parent.CTRL.shad_clip, falloff*bias * (-parent.CTRL.shadow_bias+1))"} {"start ? 1 : min((1 - (falloff*(1-bias))), (1-parent.CTRL.highlight_clip))"} {"start ? 1 : max(C, 1-parent.CTRL.highlight_clip)"}}
name Keyer3
xpos 290
ypos 296
addUserKnob {20 User}
addUserKnob {3 number}
number 3
addUserKnob {7 falloff}
falloff {{parent.CTRL.key_falloff}}
addUserKnob {7 bias}
bias {{"start ? 1 : end ? 0 : (1-log_exp)*parent.CTRL.key_bias"}}
addUserKnob {7 log_exp}
log_exp {{"(1 - log(exposure)/7)/2"}}
addUserKnob {7 exposure}
exposure {{"base_exposure / \[metadata input/exposure_time]"}}
addUserKnob {7 base_exposure}
base_exposure {{parent.CTRL.base_exposure}}
addUserKnob {6 start +STARTLINE}
start {{"number == 0"}}
addUserKnob {6 end +STARTLINE}
end {{"number == (parent.CTRL.exposures-1)"}}
}
Multiply {
channels rgb
value {{"base_exp / \[metadata input/exposure_time]"}}
name Multiply3
label "\[value value]"
xpos 290
ypos 368
addUserKnob {20 BaseExp}
addUserKnob {7 base_exp}
base_exp {{parent.CTRL.base_exposure}}
}
Blur {
channels alpha
size {{parent.CTRL.alpha_blur}}
name Blur5
xpos 290
ypos 440
}
Premult {
name Premult3
xpos 290
ypos 518
}
Input {
inputs 0
name Input2
xpos 180
ypos 206
number 2
}
Keyer {
operation "luminance key"
range {{"end ? 0 : min(B, parent.CTRL.shad_clip)"} {"end ? 0 : max(parent.CTRL.shad_clip, falloff*bias * (-parent.CTRL.shadow_bias+1))"} {"start ? 1 : min((1 - (falloff*(1-bias))), (1-parent.CTRL.highlight_clip))"} {"start ? 1 : max(C, 1-parent.CTRL.highlight_clip)"}}
name Keyer2
xpos 180
ypos 296
addUserKnob {20 User}
addUserKnob {3 number}
number 2
addUserKnob {7 falloff}
falloff {{parent.CTRL.key_falloff}}
addUserKnob {7 bias}
bias {{"start ? 1 : end ? 0 : (1-log_exp)*parent.CTRL.key_bias"}}
addUserKnob {7 log_exp}
log_exp {{"(1 - log(exposure)/7)/2"}}
addUserKnob {7 exposure}
exposure {{"base_exposure / \[metadata input/exposure_time]"}}
addUserKnob {7 base_exposure}
base_exposure {{parent.CTRL.base_exposure}}
addUserKnob {6 start +STARTLINE}
start {{"number == 0"}}
addUserKnob {6 end +STARTLINE}
end {{"number == (parent.CTRL.exposures-1)"}}
}
Multiply {
channels rgb
value {{"base_exp / \[metadata input/exposure_time]"}}
name Multiply4
label "\[value value]"
xpos 180
ypos 368
addUserKnob {20 BaseExp}
addUserKnob {7 base_exp}
base_exp {{parent.CTRL.base_exposure}}
}
Blur {
channels alpha
size {{parent.CTRL.alpha_blur}}
name Blur6
xpos 180
ypos 440
}
Premult {
name Premult4
xpos 180
ypos 518
}
push 0
Input {
inputs 0
name Input1
xpos 70
ypos 206
number 1
}
Keyer {
operation "luminance key"
range {{"end ? 0 : min(B, parent.CTRL.shad_clip)"} {"end ? 0 : max(parent.CTRL.shad_clip, falloff*bias * (-parent.CTRL.shadow_bias+1))"} {"start ? 1 : min((1 - (falloff*(1-bias))), (1-parent.CTRL.highlight_clip))"} {"start ? 1 : max(C, 1-parent.CTRL.highlight_clip)"}}
name Keyer1
xpos 70
ypos 296
addUserKnob {20 User}
addUserKnob {3 number}
number 1
addUserKnob {7 falloff}
falloff {{parent.CTRL.key_falloff}}
addUserKnob {7 bias}
bias {{"start ? 1 : end ? 0 : (1-log_exp)*parent.CTRL.key_bias"}}
addUserKnob {7 log_exp}
log_exp {{"(1 - log(exposure)/7)/2"}}
addUserKnob {7 exposure}
exposure {{"base_exposure / \[metadata input/exposure_time]"}}
addUserKnob {7 base_exposure}
base_exposure {{parent.CTRL.base_exposure}}
addUserKnob {6 start +STARTLINE}
start {{"number == 0"}}
addUserKnob {6 end +STARTLINE}
end {{"number == (parent.CTRL.exposures-1)"}}
}
Multiply {
channels rgb
value {{"base_exp / \[metadata input/exposure_time]"}}
name Multiply5
label "\[value value]"
xpos 70
ypos 368
addUserKnob {20 BaseExp}
addUserKnob {7 base_exp}
base_exp {{parent.CTRL.base_exposure}}
}
Blur {
channels alpha
size {{parent.CTRL.alpha_blur}}
name Blur7
xpos 70
ypos 440
}
Premult {
name Premult5
xpos 70
ypos 518
}
Input {
inputs 0
name Input0
xpos -40
ypos 206
}
Keyer {
operation "luminance key"
range {{"end ? 0 : min(B, parent.CTRL.shad_clip)"} {"end ? 0 : max(parent.CTRL.shad_clip, falloff*bias * (-parent.CTRL.shadow_bias+1))"} {"start ? 1 : min((1 - (falloff*(1-bias))), (1-parent.CTRL.highlight_clip))"} {"start ? 1 : max(C, 1-parent.CTRL.highlight_clip)"}}
name Keyer0
xpos -40
ypos 296
addUserKnob {20 User}
addUserKnob {3 number}
addUserKnob {7 falloff}
falloff {{parent.CTRL.key_falloff}}
addUserKnob {7 bias}
bias {{"start ? 1 : end ? 0 : (1-log_exp)*parent.CTRL.key_bias"}}
addUserKnob {7 log_exp}
log_exp {{"(1 - log(exposure)/7)/2"}}
addUserKnob {7 exposure}
exposure {{"base_exposure / \[metadata input/exposure_time]"}}
addUserKnob {7 base_exposure}
base_exposure {{parent.CTRL.base_exposure}}
addUserKnob {6 start +STARTLINE}
start {{"number == 0"}}
addUserKnob {6 end +STARTLINE}
end {{"number == (parent.CTRL.exposures-1)"}}
}
Multiply {
channels rgb
value {{"base_exp / \[metadata input/exposure_time]"}}
name Multiply134
label "\[value value]"
xpos -40
ypos 368
addUserKnob {20 BaseExp}
addUserKnob {7 base_exp}
base_exp {{parent.CTRL.base_exposure}}
}
Blur {
channels alpha
size {{parent.CTRL.alpha_blur}}
name Blur1
xpos -40
ypos 440
}
Premult {
name Premult159
xpos -40
ypos 518
}
Merge2 {
inputs 7+1
operation plus
name Merge36
xpos -40
ypos 710
}
set N10a4ad90 [stack 0]
Shuffle {
red alpha
green alpha
blue alpha
name Shuffle22
label "\[value in] -> \[value out]"
xpos -150
ypos 704
}
push $N10a4ad90
MergeExpression {
inputs 2
expr0 "isnan(Br/Ar) ? 0 : Br/Ar"
expr1 "isnan(Bg/Ag) ? 0 : Bg/Ag"
expr2 "isnan(Bb/Ab) ? 0 : Bb/Ab"
channel3 alpha
expr3 "isnan(Ba/Aa) ? 0 : Ba/Aa"
name MergeDivideReverse
xpos -40
ypos 782
}
Dot {
name CTRL
tile_color 0xff0000ff
label " Controller"
note_font "Helvetica Bold Bold Bold Bold Bold Bold Bold Bold Bold Bold Bold Bold Bold Bold Bold"
note_font_size 24
note_font_color 0xa5a5a501
xpos -6
ypos 858
addUserKnob {20 User}
addUserKnob {3 exposures}
exposures 7
addUserKnob {6 grey_panelDropped l "panel dropped state" -STARTLINE +HIDDEN}
addUserKnob {7 base_exposure l base_exp}
base_exposure 0.1
addUserKnob {26 ""}
addUserKnob {26 label_keyer l " " T "Keyer Controls"}
addUserKnob {7 highlight_clip R 0 0.1}
highlight_clip 0.0665
addUserKnob {7 shad_clip R 0 0.1}
shad_clip 0.003
addUserKnob {26 ""}
addUserKnob {7 shadow_bias}
shadow_bias 0.285
addUserKnob {7 key_falloff}
key_falloff 0.4
addUserKnob {7 key_bias}
key_bias 0.615
addUserKnob {7 alpha_blur R 0 300}
addUserKnob {20 keyer_ranges l "keyer ranges" n 1}
keyer_ranges 0
addUserKnob {41 i0 T Keyer0.range}
addUserKnob {41 i1 T Keyer1.range}
addUserKnob {41 i2 T Keyer2.range}
addUserKnob {41 i3 T Keyer3.range}
addUserKnob {41 i4 T Keyer4.range}
addUserKnob {41 i5 T Keyer5.range}
addUserKnob {41 i6 T Keyer6.range}
addUserKnob {20 endGroup n -1}
}
Group {
name diffuse_grey
tile_color 0x666666ff
selected true
xpos -40
ypos 902
addUserKnob {20 DiffuseGrey}
addUserKnob {41 src T Grade.whitepoint}
addUserKnob {41 dst T Grade.white}
}
Input {
inputs 0
name Input
xpos -40
ypos 614
}
Grade {
whitepoint 0.18
white {0.1907965839 0.1917107403 0.1929099113 1}
name Grade
xpos -40
ypos 686
}
Output {
name Output
xpos -40
ypos 758
}
end_group
Output {
name Output
xpos -40
ypos 998
}
end_group
push $cut_paste_input
Group {
name ExposureBracketBlender8
label "\[value exposures] Exposures"
selected true
xpos -1360
ypos 992
addUserKnob {20 Blend}
addUserKnob {22 sort_selected_reads l "Sort Selected Reads" t "Sort selected read nodes by exposure time, and create an ExposureBracketBlender for each exposure set.\n\n" T "import nuke\nimport os, math\nfrom datetime import datetime\n\ndef hex_color(r, g=None, b=None):\n # Return hex code for given rgb tuple. g and b are optional. If not specified, a greyscale value will be returned.\n # r g and b should be between 0 and 1\n if not g:\n g = r\n if not b:\n b = r\n return int('%02x%02x%02x%02x' % (r*255,g*255,b*255,1), 16)\n\ndef unselect():\n _ = \[n.setSelected(False) for n in nuke.allNodes(recurseGroups=True)]\n\ndef sort():\n # For selected read nodes:\n # Sort by exposure time, seperate by bracked set\n\n grid_x = int(nuke.toNode('preferences').knob('GridWidth').value())\n grid_y = int(nuke.toNode('preferences').knob('GridHeight').value())\n\n node = nuke.thisNode()\n\n nuke.root().begin()\n nodes = nuke.selectedNodes('Read')\n if not nodes:\n nuke.message(\"Please select a number of read nodes to sort.\")\n return\n\n unselect()\n node.setSelected(True)\n nuke.nodeCopy('%clipboard')\n unselect()\n\n task = nuke.ProgressTask('Sorting \{0\} Exposure Brackets'.format(len(nodes)))\n\n\n # http://strftime.org\n data = \{n:(n.metadata().get('input/exposure_time'), datetime.strptime(n.metadata().get('exr/Exif:DateTimeOriginal'), r'%Y:%m:%d %H:%M:%S')) for n in nodes\}\n\n # bracket_sets is a list that will contain lists of exposure bracket sets\n bracket_sets = \[]\n bracket = \[]\n\n # Loop through all images, group into exposure brackets by calculating time difference\n data_by_time = sorted(data.items(), key=lambda x: x\[1]\[1])\n\n for i, exposure in enumerate(data_by_time):\n read = exposure\[0]\n etime = exposure\[1]\[0]\n ctime = exposure\[1]\[1]\n task.setProgress( int( float(i) / len(data_by_time) * 100) )\n if task.isCancelled():\n break\n task.setMessage('Getting next exposure time: \\t\{0\}'.format(read.name()))\n if i < len(data_by_time)-1:\n next_ctime = data_by_time\[i+1]\[1]\[1]\n\n task.setMessage('Calc time diff: \\t\{0\}'.format(read.name()))\n time_diff = next_ctime - ctime\n\n task.setMessage('Printing info: \\t\{0\}'.format(read.name()))\n # print \"===> \", read.name()\n # print etime\n # print ctime.isoformat()\n # print time_diff.total_seconds()\n # print\n\n\n task.setMessage('Append lists: \\t\{0\}'.format(read.name()))\n if time_diff.total_seconds() > 2.0:\n # print \"New bracket Detected!\\n------------------------------------------------------------\\n\\n\"\n bracket.append((read, etime, ctime, time_diff.total_seconds()))\n bracket_sets.append(bracket)\n bracket = \[]\n else:\n bracket.append((read, etime, ctime, time_diff.total_seconds()))\n # Add last images to the last exposure set\n if i == len(data_by_time)-1:\n bracket_sets.append(bracket)\n bracket = \[]\n\n\n pos = (node.xpos(), node.ypos() - grid_y*12)\n # Loop through each exposure set, sort by exposure time dark to bright\n # Sort horizontally\n for i, eset in enumerate(bracket_sets):\n if task.isCancelled():\n break\n esetsort = sorted(eset, key=lambda x: x\[1])\n set_size = len(esetsort)\n print \"Exposure set \{0\} contains \{1\} images: \\n\\t\{2\}\".format(i, set_size, \"\\n\\t\".join(\[\"\{0\} \\t\\t- \{1\} \\t: \{2\} \\t- \{3\}\".format(e\[0].name(), os.path.basename(e\[0]\['file'].getValue()), e\[1], e\[2]) for e in esetsort]))\n\n task.setMessage('Sorting exposure set: \\t\{0\}'.format(i))\n # Create new exposure blender if we have a complete exposure set\n if set_size > 1:\n nuke.nodePaste(\"%clipboard\")\n new_brack = nuke.selectedNode()\n unselect()\n new_brack.setXYpos(pos\[0], pos\[1]+grid_y*10)\n new_brack\['exposures'].setValue(set_size)\n # Set base exposure to 1 up from middle frame\n # new_brack\['base_exposure'].setValue(esetsort\[-2]\[1])\n new_brack\['base_exposure'].setValue(esetsort\[int(math.ceil(float(set_size)/2))]\[1])\n\n for j, e in enumerate(esetsort):\n if task.isCancelled():\n break\n cur_node = e\[0]\n etime = e\[1]\n ctime = e\[2]\n cur_node.setXYpos(pos\[0], pos\[1])\n cur_node\['tile_color'].setValue(hex_color(float(j)/set_size*0.8+0.2))\n if set_size > 1:\n new_brack.connectInput(0, cur_node)\n\n pos = (pos\[0] + grid_x, pos\[1])\n # space between big exposure time diffs\n # if e\[3] > 100:\n # pos = (pos\[0] + grid_x*4, pos\[1])\n\n pos = (pos\[0] + grid_x, pos\[1])\n\n nuke.delete(node)\nsort()" +STARTLINE}
addUserKnob {22 ungroup l Ungroup t "Ungroup this ExposureBracketBlender so you can manipulate the graph more easily.\n\nUseful if you need to keep only pieces of certain exposures, for example if there is motion in the frame." -STARTLINE T "def expand_group():\n node = nuke.thisNode()\n nuke.root().begin()\n _ = \[n.setSelected(False) for n in nuke.allNodes(recurseGroups=True)]\n node.setSelected(True)\n # reverses the order of the inputs...\n #nuke.expandSelectedGroup()\n node.begin()\n _ = \[n.setSelected(True) for n in nuke.allNodes()]\n nuke.nodeCopy('%clipboard')\n nuke.root().begin()\n _ = \[n.setSelected(False) for n in nuke.allNodes(recurseGroups=True)]\n nuke.nodePaste('%clipboard')\n copied_nodes = nuke.selectedNodes()\n _ = \[n.setSelected(False) for n in nuke.allNodes(recurseGroups=True)]\n print \[n.name() for n in copied_nodes]\n input0 = nuke.toNode('Input0')\n offset = (input0.xpos() - node.xpos(), input0.ypos() - node.ypos())\n print offset\n _ = \[n.setXYpos(n.xpos() - offset\[0], n.ypos() - offset\[1]) for n in copied_nodes]\n for i in xrange(node.inputs()):\n inputNode = nuke.toNode('Input\{0\}'.format(i))\n inputNode.setInput(0, node.input(i))\n _ = \[nuke.delete(n) for n in copied_nodes if n.Class() in \['Input', 'Output']]\nexpand_group()"}
addUserKnob {26 ""}
addUserKnob {41 exposures T CTRL.exposures}
addUserKnob {41 base_exposure l base_exp T CTRL.base_exposure}
addUserKnob {41 src l grey t "Sample diffuse grey to expose your image (for example the 18% grey patch on a macbeth chart)." T diffuse_grey.src}
addUserKnob {26 label_keyer l " "}
addUserKnob {41 highlight_clip T CTRL.highlight_clip}
addUserKnob {41 shad_clip T CTRL.shad_clip}
addUserKnob {26 _1 l " "}
addUserKnob {41 shadow_bias T CTRL.shadow_bias}
addUserKnob {41 key_falloff T CTRL.key_falloff}
addUserKnob {41 key_bias T CTRL.key_bias}
addUserKnob {41 alpha_blur T CTRL.alpha_blur}
addUserKnob {20 keyer_ranges l "keyer ranges" n 1}
keyer_ranges 0
addUserKnob {41 i0 T CTRL.i0}
addUserKnob {41 i1 T CTRL.i1}
addUserKnob {41 i2 T CTRL.i2}
addUserKnob {41 i3 T CTRL.i3}
addUserKnob {41 i4 T CTRL.i4}
addUserKnob {41 i5 T CTRL.i5}
addUserKnob {41 i6 T CTRL.i6}
addUserKnob {20 endGroup n -1}
}
Input {
inputs 0
name Input6
xpos 620
ypos 206
number 6
}
Keyer {
operation "luminance key"
range {{"end ? 0 : min(B, parent.CTRL.shad_clip)"} {"end ? 0 : max(parent.CTRL.shad_clip, falloff*bias * (-parent.CTRL.shadow_bias+1))"} {"start ? 1 : min((1 - (falloff*(1-bias))), (1-parent.CTRL.highlight_clip))"} {"start ? 1 : max(C, 1-parent.CTRL.highlight_clip)"}}
name Keyer6
xpos 620
ypos 296
addUserKnob {20 User}
addUserKnob {3 number}
number 6
addUserKnob {7 falloff}
falloff {{parent.CTRL.key_falloff}}
addUserKnob {7 bias}
bias {{"start ? 1 : end ? 0 : (1-log_exp)*parent.CTRL.key_bias"}}
addUserKnob {7 log_exp}
log_exp {{"(1 - log(exposure)/7)/2"}}
addUserKnob {7 exposure}
exposure {{"base_exposure / \[metadata input/exposure_time]"}}
addUserKnob {7 base_exposure}
base_exposure {{parent.CTRL.base_exposure}}
addUserKnob {6 start +STARTLINE}
start {{"number == 0"}}
addUserKnob {6 end +STARTLINE}
end {{"number == (parent.CTRL.exposures-1)"}}
}
Multiply {
channels rgb
value {{"base_exp / \[metadata input/exposure_time]"}}
name Multiply6
label "\[value value]"
xpos 620
ypos 368
addUserKnob {20 BaseExp}
addUserKnob {7 base_exp}
base_exp {{parent.CTRL.base_exposure}}
}
Blur {
channels alpha
size {{parent.CTRL.alpha_blur}}
name Blur2
xpos 620
ypos 440
}
Premult {
name Premult6
xpos 620
ypos 518
}
Input {
inputs 0
name Input5
xpos 510
ypos 206
number 5
}
Keyer {
operation "luminance key"
range {{"end ? 0 : min(B, parent.CTRL.shad_clip)"} {"end ? 0 : max(parent.CTRL.shad_clip, falloff*bias * (-parent.CTRL.shadow_bias+1))"} {"start ? 1 : min((1 - (falloff*(1-bias))), (1-parent.CTRL.highlight_clip))"} {"start ? 1 : max(C, 1-parent.CTRL.highlight_clip)"}}
name Keyer5
xpos 510
ypos 296
addUserKnob {20 User}
addUserKnob {3 number}
number 5
addUserKnob {7 falloff}
falloff {{parent.CTRL.key_falloff}}
addUserKnob {7 bias}
bias {{"start ? 1 : end ? 0 : (1-log_exp)*parent.CTRL.key_bias"}}
addUserKnob {7 log_exp}
log_exp {{"(1 - log(exposure)/7)/2"}}
addUserKnob {7 exposure}
exposure {{"base_exposure / \[metadata input/exposure_time]"}}
addUserKnob {7 base_exposure}
base_exposure {{parent.CTRL.base_exposure}}
addUserKnob {6 start +STARTLINE}
start {{"number == 0"}}
addUserKnob {6 end +STARTLINE}
end {{"number == (parent.CTRL.exposures-1)"}}
}
Multiply {
channels rgb
value {{"base_exp / \[metadata input/exposure_time]"}}
name Multiply1
label "\[value value]"
xpos 510
ypos 368
addUserKnob {20 BaseExp}
addUserKnob {7 base_exp}
base_exp {{parent.CTRL.base_exposure}}
}
Blur {
channels alpha
size {{parent.CTRL.alpha_blur}}
name Blur3
xpos 510
ypos 440
}
Premult {
name Premult1
xpos 510
ypos 518
}
Input {
inputs 0
name Input4
xpos 400
ypos 206
number 4
}
Keyer {
operation "luminance key"
range {{"end ? 0 : min(B, parent.CTRL.shad_clip)"} {"end ? 0 : max(parent.CTRL.shad_clip, falloff*bias * (-parent.CTRL.shadow_bias+1))"} {"start ? 1 : min((1 - (falloff*(1-bias))), (1-parent.CTRL.highlight_clip))"} {"start ? 1 : max(C, 1-parent.CTRL.highlight_clip)"}}
name Keyer4
xpos 400
ypos 296
addUserKnob {20 User}
addUserKnob {3 number}
number 4
addUserKnob {7 falloff}
falloff {{parent.CTRL.key_falloff}}
addUserKnob {7 bias}
bias {{"start ? 1 : end ? 0 : (1-log_exp)*parent.CTRL.key_bias"}}
addUserKnob {7 log_exp}
log_exp {{"(1 - log(exposure)/7)/2"}}
addUserKnob {7 exposure}
exposure {{"base_exposure / \[metadata input/exposure_time]"}}
addUserKnob {7 base_exposure}
base_exposure {{parent.CTRL.base_exposure}}
addUserKnob {6 start +STARTLINE}
start {{"number == 0"}}
addUserKnob {6 end +STARTLINE}
end {{"number == (parent.CTRL.exposures-1)"}}
}
Multiply {
channels rgb
value {{"base_exp / \[metadata input/exposure_time]"}}
name Multiply2
label "\[value value]"
xpos 400
ypos 368
addUserKnob {20 BaseExp}
addUserKnob {7 base_exp}
base_exp {{parent.CTRL.base_exposure}}
}
Blur {
channels alpha
size {{parent.CTRL.alpha_blur}}
name Blur4
xpos 400
ypos 440
}
Premult {
name Premult2
xpos 400
ypos 518
}
Input {
inputs 0
name Input3
xpos 290
ypos 206
number 3
}
Keyer {
operation "luminance key"
range {{"end ? 0 : min(B, parent.CTRL.shad_clip)"} {"end ? 0 : max(parent.CTRL.shad_clip, falloff*bias * (-parent.CTRL.shadow_bias+1))"} {"start ? 1 : min((1 - (falloff*(1-bias))), (1-parent.CTRL.highlight_clip))"} {"start ? 1 : max(C, 1-parent.CTRL.highlight_clip)"}}
name Keyer3
xpos 290
ypos 296
addUserKnob {20 User}
addUserKnob {3 number}
number 3
addUserKnob {7 falloff}
falloff {{parent.CTRL.key_falloff}}
addUserKnob {7 bias}
bias {{"start ? 1 : end ? 0 : (1-log_exp)*parent.CTRL.key_bias"}}
addUserKnob {7 log_exp}
log_exp {{"(1 - log(exposure)/7)/2"}}
addUserKnob {7 exposure}
exposure {{"base_exposure / \[metadata input/exposure_time]"}}
addUserKnob {7 base_exposure}
base_exposure {{parent.CTRL.base_exposure}}
addUserKnob {6 start +STARTLINE}
start {{"number == 0"}}
addUserKnob {6 end +STARTLINE}
end {{"number == (parent.CTRL.exposures-1)"}}
}
Multiply {
channels rgb
value {{"base_exp / \[metadata input/exposure_time]"}}
name Multiply3
label "\[value value]"
xpos 290
ypos 368
addUserKnob {20 BaseExp}
addUserKnob {7 base_exp}
base_exp {{parent.CTRL.base_exposure}}
}
Blur {
channels alpha
size {{parent.CTRL.alpha_blur}}
name Blur5
xpos 290
ypos 440
}
Premult {
name Premult3
xpos 290
ypos 518
}
Input {
inputs 0
name Input2
xpos 180
ypos 206
number 2
}
Keyer {
operation "luminance key"
range {{"end ? 0 : min(B, parent.CTRL.shad_clip)"} {"end ? 0 : max(parent.CTRL.shad_clip, falloff*bias * (-parent.CTRL.shadow_bias+1))"} {"start ? 1 : min((1 - (falloff*(1-bias))), (1-parent.CTRL.highlight_clip))"} {"start ? 1 : max(C, 1-parent.CTRL.highlight_clip)"}}
name Keyer2
xpos 180
ypos 296
addUserKnob {20 User}
addUserKnob {3 number}
number 2
addUserKnob {7 falloff}
falloff {{parent.CTRL.key_falloff}}
addUserKnob {7 bias}
bias {{"start ? 1 : end ? 0 : (1-log_exp)*parent.CTRL.key_bias"}}
addUserKnob {7 log_exp}
log_exp {{"(1 - log(exposure)/7)/2"}}
addUserKnob {7 exposure}
exposure {{"base_exposure / \[metadata input/exposure_time]"}}
addUserKnob {7 base_exposure}
base_exposure {{parent.CTRL.base_exposure}}
addUserKnob {6 start +STARTLINE}
start {{"number == 0"}}
addUserKnob {6 end +STARTLINE}
end {{"number == (parent.CTRL.exposures-1)"}}
}
Multiply {
channels rgb
value {{"base_exp / \[metadata input/exposure_time]"}}
name Multiply4
label "\[value value]"
xpos 180
ypos 368
addUserKnob {20 BaseExp}
addUserKnob {7 base_exp}
base_exp {{parent.CTRL.base_exposure}}
}
Blur {
channels alpha
size {{parent.CTRL.alpha_blur}}
name Blur6
xpos 180
ypos 440
}
Premult {
name Premult4
xpos 180
ypos 518
}
push 0
Input {
inputs 0
name Input1
xpos 70
ypos 206
number 1
}
Keyer {
operation "luminance key"
range {{"end ? 0 : min(B, parent.CTRL.shad_clip)"} {"end ? 0 : max(parent.CTRL.shad_clip, falloff*bias * (-parent.CTRL.shadow_bias+1))"} {"start ? 1 : min((1 - (falloff*(1-bias))), (1-parent.CTRL.highlight_clip))"} {"start ? 1 : max(C, 1-parent.CTRL.highlight_clip)"}}
name Keyer1
xpos 70
ypos 296
addUserKnob {20 User}
addUserKnob {3 number}
number 1
addUserKnob {7 falloff}
falloff {{parent.CTRL.key_falloff}}
addUserKnob {7 bias}
bias {{"start ? 1 : end ? 0 : (1-log_exp)*parent.CTRL.key_bias"}}
addUserKnob {7 log_exp}
log_exp {{"(1 - log(exposure)/7)/2"}}
addUserKnob {7 exposure}
exposure {{"base_exposure / \[metadata input/exposure_time]"}}
addUserKnob {7 base_exposure}
base_exposure {{parent.CTRL.base_exposure}}
addUserKnob {6 start +STARTLINE}
start {{"number == 0"}}
addUserKnob {6 end +STARTLINE}
end {{"number == (parent.CTRL.exposures-1)"}}
}
Multiply {
channels rgb
value {{"base_exp / \[metadata input/exposure_time]"}}
name Multiply5
label "\[value value]"
xpos 70
ypos 368
addUserKnob {20 BaseExp}
addUserKnob {7 base_exp}
base_exp {{parent.CTRL.base_exposure}}
}
Blur {
channels alpha
size {{parent.CTRL.alpha_blur}}
name Blur7
xpos 70
ypos 440
}
Premult {
name Premult5
xpos 70
ypos 518
}
Input {
inputs 0
name Input0
xpos -40
ypos 206
}
Keyer {
operation "luminance key"
range {{"end ? 0 : min(B, parent.CTRL.shad_clip)"} {"end ? 0 : max(parent.CTRL.shad_clip, falloff*bias * (-parent.CTRL.shadow_bias+1))"} {"start ? 1 : min((1 - (falloff*(1-bias))), (1-parent.CTRL.highlight_clip))"} {"start ? 1 : max(C, 1-parent.CTRL.highlight_clip)"}}
name Keyer0
xpos -40
ypos 296
addUserKnob {20 User}
addUserKnob {3 number}
addUserKnob {7 falloff}
falloff {{parent.CTRL.key_falloff}}
addUserKnob {7 bias}
bias {{"start ? 1 : end ? 0 : (1-log_exp)*parent.CTRL.key_bias"}}
addUserKnob {7 log_exp}
log_exp {{"(1 - log(exposure)/7)/2"}}
addUserKnob {7 exposure}
exposure {{"base_exposure / \[metadata input/exposure_time]"}}
addUserKnob {7 base_exposure}
base_exposure {{parent.CTRL.base_exposure}}
addUserKnob {6 start +STARTLINE}
start {{"number == 0"}}
addUserKnob {6 end +STARTLINE}
end {{"number == (parent.CTRL.exposures-1)"}}
}
Multiply {
channels rgb
value {{"base_exp / \[metadata input/exposure_time]"}}
name Multiply134
label "\[value value]"
xpos -40
ypos 368
addUserKnob {20 BaseExp}
addUserKnob {7 base_exp}
base_exp {{parent.CTRL.base_exposure}}
}
Blur {
channels alpha
size {{parent.CTRL.alpha_blur}}
name Blur1
xpos -40
ypos 440
}
Premult {
name Premult159
xpos -40
ypos 518
}
Merge2 {
inputs 7+1
operation plus
name Merge36
xpos -40
ypos 710
}
set N10b95960 [stack 0]
Shuffle {
red alpha
green alpha
blue alpha
name Shuffle22
label "\[value in] -> \[value out]"
xpos -150
ypos 704
}
push $N10b95960
MergeExpression {
inputs 2
expr0 "isnan(Br/Ar) ? 0 : Br/Ar"
expr1 "isnan(Bg/Ag) ? 0 : Bg/Ag"
expr2 "isnan(Bb/Ab) ? 0 : Bb/Ab"
channel3 alpha
expr3 "isnan(Ba/Aa) ? 0 : Ba/Aa"
name MergeDivideReverse
xpos -40
ypos 782
}
Dot {
name CTRL
tile_color 0xff0000ff
label " Controller"
note_font "Helvetica Bold Bold Bold Bold Bold Bold Bold Bold Bold Bold Bold Bold Bold Bold Bold Bold Bold"
note_font_size 24
note_font_color 0xa5a5a501
xpos -6
ypos 858
addUserKnob {20 User}
addUserKnob {3 exposures}
exposures 7
addUserKnob {6 grey_panelDropped l "panel dropped state" -STARTLINE +HIDDEN}
addUserKnob {7 base_exposure l base_exp}
base_exposure 0.1
addUserKnob {26 ""}
addUserKnob {26 label_keyer l " " T "Keyer Controls"}
addUserKnob {7 highlight_clip R 0 0.1}
highlight_clip 0.0665
addUserKnob {7 shad_clip R 0 0.1}
shad_clip 0.003
addUserKnob {26 ""}
addUserKnob {7 shadow_bias}
shadow_bias 0.285
addUserKnob {7 key_falloff}
key_falloff 0.4
addUserKnob {7 key_bias}
key_bias 0.615
addUserKnob {7 alpha_blur R 0 300}
addUserKnob {20 keyer_ranges l "keyer ranges" n 1}
keyer_ranges 0
addUserKnob {41 i0 T Keyer0.range}
addUserKnob {41 i1 T Keyer1.range}
addUserKnob {41 i2 T Keyer2.range}
addUserKnob {41 i3 T Keyer3.range}
addUserKnob {41 i4 T Keyer4.range}
addUserKnob {41 i5 T Keyer5.range}
addUserKnob {41 i6 T Keyer6.range}
addUserKnob {20 endGroup n -1}
}
Group {
name diffuse_grey
tile_color 0x666666ff
selected true
xpos -40
ypos 902
addUserKnob {20 DiffuseGrey}
addUserKnob {41 src T Grade.whitepoint}
addUserKnob {41 dst T Grade.white}
}
Input {
inputs 0
name Input
xpos -40
ypos 614
}
Grade {
whitepoint 0.18
white {0.1907965839 0.1917107403 0.1929099113 1}
name Grade
xpos -40
ypos 686
}
Output {
name Output
xpos -40
ypos 758
}
end_group
Output {
name Output
xpos -40
ypos 998
}
end_group
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.