# Refresh Viewer Node's image data when pass index of ID Mask node has changed in python

I followed the answer of this question in order to get a bounding box of visible area of an object in a rendered image. Since I have multiple objects that I add in the scene dynamically through script, and assign each of them a different pass_index, I use the following function (again, adapted from here) to get the bounding box of each object:

def getVisibleBoundingBox(objectPassIndex):

S = bpy.context.scene
width  = int( S.render.resolution_x * S.render.resolution_percentage / 100 )
height = int( S.render.resolution_y * S.render.resolution_percentage / 100 )
depth  = 4
#set the index to the object's pass index

#render.render is very slow, try to find alternative to update the Viewer Node
#bpy.ops.render.render()

pixels = np.array( bpy.data.images['Viewer Node'].pixels[:] ).reshape( [height, width, depth] )
# Keep only one value for each pixel (white pixels have 1 in all RGBA channels anyway), thus converting the image to black and white
pixels = np.array( [ [ pixel[0] for pixel in row ] for row in pixels ] )

bbox = np.argwhere( pixels )
(ystart, xstart), (ystop, xstop) = bbox.min(0), bbox.max(0) + 1
return xstart, xstop, height - ystart, height - ystop


The problem is that the Viewer Node image data does not update when I change the index of the ID Mask node with:

 bpy.context.scene.node_tree.nodes['ID Mask'].index = objectPassIndex


and it contains stale data from another object's pass index, so I get the same result all the time for different objectPassIndex when I try to take the pixels with:

pixels = np.array( [ [ pixel[0] for pixel in row ] for row in pixels ] )


This is not the case if I change the pass index in the GUI, where the Viewer node updates immediately. A workaround I found is to call bpy.ops.render.render() after changing the index and before taking the pixels, however, this operation is very slow and therefore not applicable for my use case. I also tried calling bpy.data.images['Viewer Node'].reload() without success.

So, the question is, is there a faster way to refresh the data of the Viewer node?

If not, another alternative that might work for me is to get a combined image containing in each pixel the pass_index of the particular object. I noticed that this image can be generated in the GUI by rendering and selecting the pass to IndexOB (image attached). But I do not know if it is possible to change the pass to IndexOB from python and grab the rendered image.

So the alternative question is if there is a way to get access to the pixels of this image in python?

The answer to this question is the answer to the alternative question: "How to get the image where all pixel values are set to the pass_index of the particular object". Instead of using "ID Mask" node, the IndexOB output of "Render Layers" node can be connected directly to the Image input of "Viewer" node. Then the function can be modified like this to get the bounding box for particular objectPassIndex:

def getVisibleBoundingBox(objectPassIndex):

S = bpy.context.scene
width  = int( S.render.resolution_x * S.render.resolution_percentage / 100 )
height = int( S.render.resolution_y * S.render.resolution_percentage / 100 )
depth  = 4

pixels = np.array( bpy.data.images['Viewer Node'].pixels[:] ).reshape( [height, width, depth] )
# Keep only one value for each pixel (white pixels have 1 in all RGBA channels anyway), thus converting the image to black and white
pixels = np.array( [ [ pixel[0] for pixel in row ] for row in pixels ] )

bbox = np.argwhere( pixels == objectPassIndex )
(ystart, xstart), (ystop, xstop) = bbox.min(0), bbox.max(0) + 1
return xstart, xstop, height - ystart, height - ystop