1
$\begingroup$

I am rendering depth maps from different angles for many meshes by placing my cameras on a sphere with fixed radius. The meshes I use are all normalized (all within a sphere with radius 0.5) and centered at (0, 0, 0). After rendering I load the depth maps in Python using imageio package or scipy.ndimage.imread I noticed that each individual depth map always has a maximum value of 255. However, what I actually expected was to obtain depth maps whose values are interpretable and intuitive. By interpretability I mean a distance of 40 in one depth map rendered from mesh X will be the same as distance 40 of another depth map rendered from mesh Y. But this is not the case as of now, even within the renderings of the same mesh. To make it more clear: if I have a very small mesh whose furthest vertex would be very close to (0, 0, 0) I would still have values of 255 in the rendered depth maps. However, I will still have pixel values of 255 in the depth map renderings of another mesh which is much larger than the previous one. The same issue persists if I normalize the output values of the Z-pass: all renderings have a maximum value of 1. I'm afraid the issue is related to the PNG saving pipeline that might be doing something weird to the rendering results, as discussed here. So I wonder does anyone know if I am doing something wrong? Any thoughts/solutions would be greatly appreciated.

FYI, here's the code I use for rendering:

scene = bpy.context.scene
scene.render.image_settings.color_depth = '16'
scene.display_settings.display_device = 'sRGB'
scene.view_settings.view_transform = 'Raw'
scene.sequencer_colorspace_settings.name = 'Raw'
scene.use_nodes = True
for node in scene.node_tree.nodes:
    scene.node_tree.nodes.remove(node)
renderNode = scene.node_tree.nodes.new('CompositorNodeRLayers')

depthOutputNode = scene.node_tree.nodes.new('CompositorNodeOutputFile')
depthOutputNode.format.file_format = 'PNG'
depthOutputNode.format.color_depth = '16'
depthOutputNode.format.color_mode = 'RGB'
depthOutputNode.base_path = 'somePath/'
depthOutputNode.file_slots[0].path = 'fileNameDepth#'

scene.node_tree.links.new(renderNode.outputs[2], depthOutputNode.inputs[0])

bpy.ops.render.render(write_still=True)

Update: I just did file depthImg.png in Ubuntu and confirmed that the generated depth maps are 16-bit. It's likely that the image packages in Python do not support reading png files with more than 8 bits per channel! I tried a couple of methods and non works. Any solutions for this?

$\endgroup$
  • $\begingroup$ Can I suggest using Animation Nodes for this? One can simply use a BVH Tree to ray cast some rays and compute the distance. No images are required and it is reasonably fast. You can then continue processing in AN or pass the values to python. $\endgroup$ – Omar Emara Mar 31 '18 at 20:00
  • $\begingroup$ Also, I haven't tried this, but wouldn't sampling the values from bpy.data.images[0].pixels work? I think Float Buffer have to be enabled to get non normalized values. $\endgroup$ – Omar Emara Mar 31 '18 at 20:02
  • $\begingroup$ @OmarAhmad I have never used animation nodes so I'm not sure if that's the way to go. I just actually confirmed that the stored PNGs are 16-bits. But it seems that almost no image package in Python reads 16-bit PNGs! So it could just be a reading issue. I tried storing the original pixel values but I hit lots of obstacles and I eventually realized that reading pixel values is impossible unless Blender fixes a bug (see here). $\endgroup$ – Amir Mar 31 '18 at 20:07
  • 3
    $\begingroup$ Stop using PNGs. They are junk. Depth is data, and no display referred encoding format is going to make this easy. Just use EXRs. $\endgroup$ – troy_s Mar 31 '18 at 20:13
  • 1
    $\begingroup$ It seems to me like you keep asking the same question many times... The Z pass is not an image, it should be thought as data. If you need to visualize it, use a normalize node, but do not save that. Saving in a display referred format will mean scaling and possibly distorting the information. Keep the Z pass as linear, unscaled data in an EXR file. $\endgroup$ – cegaton Mar 31 '18 at 23:06
2
$\begingroup$

As @troy_s suggested, PNGs are not suitable for this use. PNGs are more suited to image color data only. For depth information you should use a format such as Open EXR. This can be achieved easily by simply changing the depthOutputNode.format.file_format = 'PNG' line to depthOutputNode.format.file_format = 'OPEN_EXR'.

The resultant image should now contain the correct depth information. For example, it can be loaded into the compositor, normalized, and passed to a Viewer node as shown :

show depth

$\endgroup$
  • $\begingroup$ One thing I need to be sure of is would the depth data stored in EXR format represent valid distance? I just want to make sure that you are not answering the question that has not been asked here. Just to make it clear, I am not asking if depth information is better stored in EXR or PNG. I am basically asking how I can store the distance correctly? $\endgroup$ – Amir Mar 31 '18 at 22:52
  • $\begingroup$ btw, there are two reasons on why I did not use EXR: 1- I want to be able to easily visualize the depth maps on the screen, outside Blender. 2- There is a bug in Blender that does not allow me to store EXR files as of now. $\endgroup$ – Amir Mar 31 '18 at 22:54
  • $\begingroup$ @Amir Open EXR essentially stores the ‘raw’ render data so storing as EXR retains the full render data - as if you composite directly from the render layer. For full accuracy use the highest bit depth available (rather than just 16 bit) since EXR supports more than PNG. The reported ‘bug’ in the other question is a separate issue - perhaps finding a way to work around that is the way forward here. $\endgroup$ – Rich Sedman Apr 1 '18 at 6:26

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.