Setup (reworked):
Camera position: (0, -6, 6);
Camera angles: (45, 0, 0) (zyx rotation sequence);
Camera resolution: X=64 [px]; Y=64[px];
Sensor sizes: 32 [mm] x 32 [mm];
=> px_per_mm: 2 [px/mm];
Focal length: 35 [mm];
Clipping: Start = 2 [m]; End = 16 [m];
Object: Plane orthogonal to z-axis located at z=0.
[EDIT] Used 'Blender Render' not 'Blender Cycles'.
The depth map is determined by an easy-to-compute formula, namely:
depth(x, y) = fy * sqrt(2) * L / (fy - (y - cy)) (1)
Where:
L = 6 [m] (height of the camera)
fy = focal length * px_per_mm = 70 [px]
cy = y-coordinate of the principal located in the middle of the screen,
i.e. at 32 [px]
Then:
depth(x, y) = 593.969696 / (70 - (y - 32)) (2)
Saving the rendered image into an OpenEXR file 'plane.exr' allows to access the depth- coordinate and compare it to the theoretical solution in (2). Save the script below into 'extract_this.py':
import OpenEXR, Imath
import numpy
import sys
pt = Imath.PixelType(Imath.PixelType.FLOAT)
handle = OpenEXR.InputFile("plane.exr")
dw = handle.header()['dataWindow']
x_size, y_size = (dw.max.x - dw.min.x + 1, dw.max.y - dw.min.y + 1)
array = numpy.fromstring(handle.channel('Z', pt), dtype=numpy.float32)
array.shape = (y_size, x_size)
it = numpy.nditer(array, flags=["multi_index"])
while not it.finished:
depth = float(it[0])
if depth < 10000:
y, x = it.multi_index
print x, y_size - y, depth
it.iternext()
The call it with:
> python extract_this.py > tmp.txt
will deliver the a data file with x and y in columns 1 and 2. The depth is stored in column 3. This can be displayed with gnuplot:
> depth(y) = 593.969696 / (70 - (y - 32))
> splot "tmp.txt" u 1:2:($3 - depth($2-0.5))
and delivers a picture as shown below. A '0.5' is subtracted to account for the fact that blender seems to deliver the depth at the pixel's center. As can be seen, there is a line where the z-coordinate is simply off the track. What can be done about that?