# Z buffer read from exr file is all ones

I render RGB images and their depth maps as well for a series of frames (using cycles). Specifically, I need not a B/W depth png/jpg image, rather the actual depth values itself, which I can later read off in python. I found the exr to be the oft suggested format in online forums, as I want to store float values as such.

So, I stored the rgb image as well as the z buffer together in a .exr file. In python, while I could reassemble the rgb image perfectly, I discovered the Z channel info to be a matrix of ones. It should actually contain the float depth values.
What could be going wrong here? I have checked the 'zbuf' option in output properties.

For reference, this is the code I use to separate rgb and z values from the exr file:

import sys
import numpy as np
import OpenEXR as exr
import Imath

"""Read color + depth data from EXR image file.

Parameters
----------
filename : str
File path.

Returns
-------
img : RGB or RGBA image in float32 format. Each color channel
lies within the interval [0, 1].
Color conversion from linear RGB to standard RGB is performed
internally. See https://en.wikipedia.org/wiki/SRGB#The_forward_transformation_(CIE_XYZ_to_sRGB)

Z : Depth buffer in float32 format or None if the EXR file has no Z channel.
"""
print(filename)
filename = str(filename)
exrfile = exr.InputFile(filename)

isize = (dw.max.y - dw.min.y + 1, dw.max.x - dw.min.x + 1)

channelData = dict()

# convert all channels in the image to numpy arrays
C = exrfile.channel(c, Imath.PixelType(Imath.PixelType.FLOAT))
C = np.fromstring(C, dtype=np.float32)
C = np.reshape(C, isize)

channelData[c] = C

colorChannels = ['R', 'G', 'B', 'A'] if 'A' in header['channels'] else ['R', 'G', 'B']
img = np.concatenate([channelData[c][...,np.newaxis] for c in colorChannels], axis=2)

# linear to standard RGB
img[..., :3] = np.where(img[..., :3] <= 0.0031308,
12.92 * img[..., :3],
1.055 * np.power(img[..., :3], 1 / 2.4) - 0.055)

# sanitize image to be in range [0, 1]
img = np.where(img < 0.0, 0.0, np.where(img > 1.0, 1, img))

Z = None if 'Z' not in header['channels'] else channelData['Z']

return img, Z

def main(fpth):
print(np.max(depth))
print(np.min(depth))

return

if __name__ == '__main__':

main(sys.argv[1:])

• been looking into this and the only way I could export the Z channel was to use the compositor and link image, alpha and depth to the output node before rendering and saving the exr file Sep 15 '20 at 15:25
• If you look at the edit, you can see the nodes. This was what I was using. Acc to your suggestion, should I connect Image to composite node as well as the output node? Sep 16 '20 at 17:15
• Make that file output node use openEXR as format, so that you get the depth data in a linear scale.
– susu
Sep 16 '20 at 17:20
• what i meant is plugging everything from render layer to composite node (no other nodes) was the only way i got an exr which, when opened with blender and passing depth through normalise node, actually gave me a z pass i could visualise. it also showed up in exrdisplay but looked like just 1s and 0s, nothing in between Sep 16 '20 at 19:53
• Connecting both Image and depth to composite node worked! To read the Z values, the python code mentioned in the OP changes slightly, with that info being under the channel name 'Renderlayers.Z" or something like that. Maybe you should post this as an answer. Thanks a lot! Sry for the late reply, been having some internet issues. Sep 17 '20 at 12:55

You just need to disable the compositor in Output Properties > Post Processing > Pipeline > Compositing. Basically if compositing is enabled only the Image pass gets forwarded to the composite node (even if Nodes weren't enabled) and the Depth defaults to Z=1.000
If you do need to use the compositor, make sure you also link Depth to the composite node to get an exr file with a correct Z channel: