Access Z-buffer and save it as file


(Scottie_09) #1

Hi all,
i’m new user of Blender.
I need to access Z-buffer because I need to use the deistances from the objects in the scene to the camera. Now, I can only get a image in grey-scale that gives me the object’s depth but I would like save the value of each pixel in a matrix or array and save it in a file .txt
I wonder if there is a script that does that. Otherwise, how can I write a script which does it?

Thanks for your help!!!


(cudin) #2

Hi, I want to do the same thing. I think a possible way is render an EXR image and then extract the Z channel… If anybody know any other solutions, please help us!


(Crouch) #3

If I’m not mistaken you can save the z-buffer to an image, using the nodes editor. After that it’s a simple matter of reading the values and writing them to a text file.

Alternatively you could use the z-buffer of the 3d-view. Here’s some code to do that:

import Blender
from Blender import *
from Blender.BGL import *

windows = Window.GetScreenInfo()
for w in windows:
    if w['type'] == Window.Types.VIEW3D:
        xmin, ymin, xmax, ymax = w['vertices']
        width = xmax-xmin
        height = ymax-ymin

zbuf = Buffer(GL_FLOAT, [width*height])
glReadPixels(xmin, ymin, width, height, GL_DEPTH_COMPONENT, GL_FLOAT, zbuf)

strbuf = []
for i in range(height):
    strbuf.append(str(zbuf[i*width:i*width+width]))
strbuf.reverse()

file = open("C:/test.txt", "w")
for i in strbuf:
    file.write(i[1:-1]+'
')
file.close()

Some remarks:
This doesn’t work in wireframe and bounding box mode (viewport shading methods).
It’s best to have only one 3d-view when running the script.
It could be that the z-depth of the grid is also output, so you might want to disable it.


(cudin) #4

Many thanks for your answer! But i need to have a correlation between the image render (variable image size) and the z depth pixel to pixel, and I’m afraid that with glReadPixels isn’t possible. The EXR image format seems perfect for me, with an EXR viewer I can read the Z channel value referred to the pixel under the cursor. But I haven’t found nothing for saving z-values on text file. However thanks!


(gpaprmh) #5

ok, I’m not real comfortable with python (I mostly code in c…I could have coded a seq plugin to do the same thing in 1/4 the time it took me to learn enough python to do this) but this will give you a txt file with an array filled with the distance from the camera to each pixel in blender units.

  • you need a node window, UV/Image window (set to viewer node) and a text window all open at the same time
  • render
  • in the node editor, link the renderlayer z socket to a viewer image socket and make that viewer node active so that it shows in the image editor- you will see a white screen…don’t worry about it :wink:
  • load the following code into the text window and run it.
import Blender
from Blender import *

img = Image.GetCurrent()
if img:
    maxz = 50  #set just past the max depth of your scene
    xs,ys = img.getSize()
    file = open("put your path/filename here", "w")
    file.write("zbuffer_array["+str(xs*ys)+"] = {")
    for y in range(ys):
        for x in range(xs):
            r,g,b,a = img.getPixelHDR(x,y)
            if  r> maxz:
                r = maxz
            file.write("%.3f, " %r) #change number if you want more or less precision
    file.write("}
")
    file.close()
    print("done")

notes:

  • maxz gives you a better value to deal with if a pixel is the blender background… ie: 50.000 instead of 99999997952.000 (it can also make the txt file much smaller if alot of background is present- 50% with default cube)
  • if you want values 0.0->1.0 instead of actual blender units, run the z socket through a normalize node (or Map value node) before the viewer node.
  • This produces a huge text file! default cube at 720*480 is 2.6megs with 345600 array elements

(phoenixart) #6

Is there a way to have the z-buffer with anti-aliased edges instead of the jagged edges?


(Michael W) #7

It has to be aliased or else z-combine doesn’t work… it’s the “standard”

I guess if you need it smooth you just need to render it twice up an then downsample… of use your nodes with FSA

For those wanting a non python way just use openexr…

or if you want another format :
compositing nodes and link the “z” output to the output node (or a file output node…)

if you don’t want in as a float you could link the z to a “normalise” node and then to the output…

http://www.cowtoolsmedia.co.uk/zbuffer.jpg


(phoenixart) #8

Thanks for your explanation Michael W, but I’m not sure to fully understand.
Following your tips I’m trying to save the Open Exr after having “normalized” it, but I still see the jagged edges. Btw, now that you told me this has to be aliased, I see I’ve to look for a workaround…


(cudin) #9

Many thanks to all! The paprmh’s code is great. Now I’m trying to do a tool more automatic, in where the 3 preliminary steps are done by a python script. So:

  1. Is possible render in image window (setting context displayMode=0) -first step done-OK
    2)OK
    3)Without using node editor, I see that U/V image window has a button ‘Draws zbuf values mapped to camera clip…’. The result is exactly as connect Z socket to an output viewer, so it’s possible use the script for all blend file. But, unfortunately, I’havn’t found an API for set this button in a script. Any advice?

(borwa) #10

Hello. Sorry for digging up an old thread.
I’m trying to do the same thing - access Z data dircetly from script. Does enyone know how to port the gpaprmh’s code to new python API for Blender 2.64?
I have the particular problem with this part:

img = Image.GetCurrent()

EDIT:
After a few tries I have a more general question - is there a way to access render layers/passes data from python script?
I know where to find render settings and render the scene but where should I look for the result?

EDIT2:
Or maybe is there another way to save Z-Buffer data to a file (16-bit image or text)? Saving to a 8-bit grayscale PNG works great but I need a higher bit depth.