Tip: Quickly Link Noise Reduction Nodes with Python

Tip: Quickly Link Noise Reduction Nodes with Python

b°wide has provided the community with a great pack of predefined node groups you can use in many situations. They are a must-have for any more sophisticated project. You can get them here. To enable the setup, you first have to select all the channels and then connect all in- and outputs. That can be very tedious, here is a one-click-solution.

In this article I will be focusing on the amazing noise removal group. I will also assume that you have some basic skills in Python such as clicking new Text or knowing what import bpy does. If you don't feel confident at this level, I would ask you to try the ARewO tutorials, where I try to start at 0 Level.

import bpy

bpy.context.scene.render.layers["RenderLayer"].use_pass_combined = True
bpy.context.scene.render.layers["RenderLayer"].use_pass_z = True
bpy.context.scene.render.layers["RenderLayer"].use_pass_ambient_occlusion = True
bpy.context.scene.render.layers["RenderLayer"].use_pass_diffuse_direct = True
bpy.context.scene.render.layers["RenderLayer"].use_pass_diffuse_indirect = True
bpy.context.scene.render.layers["RenderLayer"].use_pass_diffuse_color = True
bpy.context.scene.render.layers["RenderLayer"].use_pass_glossy_direct = True
bpy.context.scene.render.layers["RenderLayer"].use_pass_glossy_indirect = True
bpy.context.scene.render.layers["RenderLayer"].use_pass_glossy_color = True
bpy.context.scene.render.layers["RenderLayer"].use_pass_transmission_direct = True
bpy.context.scene.render.layers["RenderLayer"].use_pass_transmission_indirect = True
bpy.context.scene.render.layers["RenderLayer"].use_pass_transmission_color = True
bpy.context.scene.render.layers["RenderLayer"].use_pass_subsurface_direct = True
bpy.context.scene.render.layers["RenderLayer"].use_pass_subsurface_indirect = True
bpy.context.scene.render.layers["RenderLayer"].use_pass_subsurface_color = True
bpy.context.scene.render.layers["RenderLayer"].use_pass_emit = True
bpy.context.scene.render.layers["RenderLayer"].use_pass_environment = True
bpy.context.scene.render.layers["RenderLayer"].use_pass_normal = True

# get the components
nt = bpy.context.scene.node_tree
render = nt.nodes['Render Layers']
reduce_noise = nt.nodes['Group']

# store all relevant (not hidden) sockets
list_out = [n.name for n in render.outputs]
list_in = [n.name for n in reduce_noise.inputs if not n.hide]

for socket in list_in: #list of names -> strings
    if socket.startswith('SSS'): # He used SSS instead of Subsurface, so we have to fix that.
        reduce_noise.inputs[socket].name = socket.replace('SSS', 'Subsurface')
        connection = socket.replace('SSS', 'Subsurface')
    else: # for all others we just leave it
        connection = socket
    
    if connection in list_out:
        #now that all out and inputs have the same name, we can link them by name
        nt.links.new(render.outputs[connection], reduce_noise.inputs[connection])

Setup:
You need to download the file given in the description above and create a new blendfile. In your new blendfile go to File -> Append (or SHIFT + F1), then navigate to the b°wide blendfile, choose it and you will see a directory representing everything inside the blendfile. Choose NodeTree and from there select PassCombineDenoiser node. Doubleclick it and go to the compositor setting or open a node editor. Under the scene node enable Use Nodes (1), choose add (or SHIFT + A) -> Group -> PassCombineDenoiser.

You will find a node group with a lot of inputs, but we only have 3 outputs on our Render Layer node, so let's change that. Go to the Render Layer tab in the Properties Window and twirl down "Passes" (1).
Before we enable the passes, move your mouse over the line between the 3D View and the Info View (at the very top, which contains File, Render etc. menu). If you drag this down, you will get your second best friend in exploring Blender Python right after the Python console. After you dragged this down enough so you can see a few lines, go back to the render passes and enable AO. Check what happens in the dragged-down field. You will actually see what Blender did in just as you clicked it. You will read:

bpy.context.scene.render.layers["RenderLayer"].use_pass_ambient_occlusion = True

Now enable the rest of the necessary passes, don't worry, you will only need to do this once, when you use the final script no-one will ever ask you to click that many similar buttons, because that's what scripts are for. Then go to the Python information window and use B to box select all necessary lines (the ones that contain use_pass_ ). With the mouse still over that window press CRTL + C to copy. Open the Text Editor (or switch to Scripting window preset), create a new script and type:

import bpy

Then paste the code you copied from the information. Now our script does the exact same thing as clicking all those buttons did. Which is a big help already, but let's take it to the next level.
We'll define a few helper variables, just to prevent overly long lines and increase readability:

nt = bpy.context.scene.node_tree
render = nt.nodes['Render Layers']
reduce_noise = nt.nodes['Group']

So you can imagine the node_tree as a directory, the "files" we actually need to use is the reduce_noise node, which is labeled 'Group' as well as the output (render) node, labeled 'Render Layers'. 
The next lines are a bit more sophisticated. You could also use for loops to go through all the in- and outputs, but this is the proper Python way.

list_out = [n.name for n in render.outputs]
list_in = [n.name for n in reduce_noise.inputs if not n.hide]

Explanation: If we want to store all the names of the outputs, we cannot use render.outputs.names, because the individual outputs each have a name, the list of outputs does not. So we use [] to indicate that it's going to be a list. n is a temporary variable that can store whatever is needed. It is necessary to store each output in n because that is how we can use n.name, which will be the individual names of the outputs. So for all outputs there are store their name in the array list_out.
The next line does the same thing, but we do not need all inputs. You can check in the console that group has more inputs than we can see, since I don't want to deal with those, I sort them out by saying: put my n.name into the list if only it's not hidden.

So we'll start a for loop since we want this to be done for (almost) all items in the list_in array.

for socket in list_in:

From now on everything contained in this loop needs a tab stop in front of it. We'll go through all items (strings) in the list_in and each will be referred to as "socket" in every cycle. You may have already noticed that all the sockets have corresponding names except the last three which read Subsurface Scattering on the left and SSS on the right. If you want to connect the passes by using the F key, that's slowing you down a bit since you'd have to connect by mouse or rename those inputs in the group. I was actually happy about this for 2 reasons: 1. Scripting this makes more sense and 2. We get to look over a bit more sophisticated handling of strings.

if socket.startswith('SSS'): # He used SSS instead of Subsurface, so we have to fix that.
        reduce_noise.inputs[socket].name = socket.replace('SSS', 'Subsurface')
        connection = socket.replace('SSS', 'Subsurface')

One of the great things about Python is that if you read the code, often it's actually easy to guess what it does without knowing much about it. So in plain English: If the current socket name starts with "SSS", I will replace "SSS" with "Subsurface" in the socket name. The connection variable stores the actual name of the in- and corresponding output, because socket is determined by the loop we should not modify it.

else: # for all others we just leave it
        connection = socket
    if connection in list_out:
        nt.links.new(render.outputs[connection], reduce_noise.inputs[connection])

Now that all out- and corresponding inputs have the same name, we can link them by name. There are a couple of parameters you can modify by hand and they don't have a corresponding output, so we will only attempt to connect those that are actually existent in the Renderlayer outputs (list_out).
It is important to note at this point that you might expect the link command inside the in- or output, but the connections actually belong to node tree itself, so we use nt which we declared to be the scene's node tree earlier.

 


Related Items:


Tutorial: Lens Flares using the Compositor
Tutorial: Background Stencils with Cycles and Compositing
Tutorial: Two Title Animations - Grunge and Sleek Rays
Tutorial: Sin City Look in Blender