Counting pixels in Blender

UPDATE 2019: the image of the node setup has been lost.

For my research, I’m using Blender to generate images. I wanted to know how visible a certain object is in the final render (i.e. how many pixels it occupies). For this there is the “object index” render pass (aka “IndexOB” in the compositor). I’ve been struggling with it, since it always outputs that the index is 0, even though there are multiple objects in the scene.

Well, with the help of mfoxdogg on the #blender IRC channel, we found a solution: You need to set the index by hand, for every object you’re interested in. If you go to the object properties (in the properties explorer), in the section “Relations” there is a slider “Pass Index”. This is set to 0 by default, and you can set it to any positive number you want. This is then reflected in the output of the “IndexOB” render pass.

So to solve my problem, I needed to count the pixels that a certain object occupies in the image. For this I connected a viewer node to the “IndexOB” pass. Once you’ve rendered, you can then access the pixel data using bpy.data.images['Viewer Node'].pixels. It’s a one-dimensional array of floats, in the RGBA format. This means that every pixel is represented as four consecutive floats.

The IndexOB data is stored in the RGB channels, and the alpha channel is set to 1.0. You could write some code to only inspect the R channel for its value and count the ones that are equal to the object index you’re interested in. I chose a more lazy approach of counting all RGBA values that are equal to the index, then dividing by 3. For this you do need an object index larger than 1, as the alpha channel is always set to 1 too. So here is the code:

import bpy

# I'm interested in the object named "L"
ob_name = 'L'

# Set the "pass index"
bpy.data.objects[ob_name].pass_index = 42

# Render the image
bpy.ops.render.render()

# Count all pixels that have value = 42. We count all R, G and B components
# separately, so divide by 3 to get the actual number of pixels
pixel_count = sum(1 for p in bpy.data.images['Viewer Node'].pixels if p == 42) / 3
print('Pixel count for object %r : %i' % (ob_name, pixel_count))
dr. Sybren A. Stüvel
dr. Sybren A. Stüvel
Open Source software developer, photographer, drummer

Related