enter image description hereI 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

def readEXR(filename):
    """Read color + depth data from EXR image file.
    filename : str
        File path.
    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)
          for more information.
    Z : Depth buffer in float32 format or None if the EXR file has no Z channel.
    filename = str(filename)
    exrfile = exr.InputFile(filename)
    header = exrfile.header()
    dw = header['dataWindow']
    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
    for c in header['channels']:
        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):
    img, depth = readEXR(fpth[-1])

if __name__ == '__main__':
  • 1
    $\begingroup$ 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 $\endgroup$
    – pevinkinel
    Sep 15, 2020 at 15:25
  • $\begingroup$ 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? $\endgroup$ Sep 16, 2020 at 17:15
  • $\begingroup$ Make that file output node use openEXR as format, so that you get the depth data in a linear scale. $\endgroup$
    – susu
    Sep 16, 2020 at 17:20
  • $\begingroup$ 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 $\endgroup$
    – pevinkinel
    Sep 16, 2020 at 19:53
  • $\begingroup$ 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. $\endgroup$ Sep 17, 2020 at 12:55

1 Answer 1


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:

result using compositor


You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .