Exporting depth maps from Python (32-bit, scaled)¶
- Status: unverified
- Applies to: Metashape Pro 2.0+. The API surface used here (
chunk.depth_maps[camera],.image(),chunk.transform.scale,Image.save()) is unchanged across the 1.6 → 2.x window. - Edition: Pro
- Diátaxis: how-to
- Confidence: medium
- Last reviewed: 2026-05-22
Confidence: medium. The
chunk.depth_mapsAPI surface and the 32-bit-float depth format are introspection-confirmed; the scaling-to-real-units recipe is forum-attested with permalink.
Problem¶
You have run Build Depth Maps in Metashape and want the raw, float32 depth-map images on disk — not the 8-bit grayscale that the GUI's File → Export → Export Depth command produces from the mesh. You want one file per camera, scaled to real-world units, and you want this from Python so it can run in a batch.
Context¶
Two distinct depth-map outputs exist in Metashape:
- GUI's Export Depth. Renders a depth image from the mesh model, in grayscale 8-bit. Suitable for inspection, lossy for any quantitative use. Requires a mesh to exist.
- Per-camera depth maps from the alignment / depth-map step.
Stored in the project's
.files/<chunk>/<frame>/depth_maps/directory as float32 EXR images, but unscaled — they are in the chunk's internal frame, not metres. Accessible viachunk.depth_maps[camera].image()from Python.
This article exports the second kind, applying
chunk.transform.scale to convert internal-frame depths to
real-world units.
Solution¶
The minimal export script¶
import Metashape
chunk = Metashape.app.document.chunk
if chunk is None:
raise SystemExit("No active chunk.")
scale = chunk.transform.scale or 1.0 # 1.0 for unreferenced chunks
output_dir = "/path/to/depth_export"
import os
os.makedirs(output_dir, exist_ok=True)
count = 0
for camera in chunk.cameras:
if camera.transform is None:
continue # not aligned
if camera.type != Metashape.Camera.Type.Regular:
continue # skip auxiliary frames
if camera not in chunk.depth_maps:
continue # no depth map computed
depth = chunk.depth_maps[camera].image() * scale # float32, scaled
depth.save(os.path.join(output_dir, f"{camera.label}.tif"))
count += 1
print(f"Exported {count} depth map(s) to {output_dir}")
The result: one <camera_label>.tif per camera, float32 1-band,
where each pixel is the distance from the camera to the surface
in the chunk's real-world units (metres, typically).
Optional: 8-bit / 16-bit grayscale variants¶
For visualisation rather than computation, normalise to a 0–1 range first and convert to U8 / U16:
img = chunk.depth_maps[camera].image() * scale # raw float32, scaled
# grayscale 8-bit (255 = farthest, 0 = nearest, similar to GUI export)
import numpy as np
arr = np.frombuffer(img.tostring(), dtype=np.float32)
lo, hi = arr.min(), arr.max()
norm = (arr - lo) / (hi - lo) if hi > lo else arr * 0
u8 = (norm * 255).astype(np.uint8).reshape(img.height, img.width)
# … save u8 with PIL or imageio …
The forum reference (insight-0016) provides a full PySide2 dialog that wraps three format options (1-band F32, Grayscale 8-bit, Grayscale 16-bit) — useful as a starting point if you want a reusable GUI tool.
Caveats and gotchas¶
chunk.depth_maps[camera]raisesKeyErrorwhen the camera has no depth map. Always test withcamera in chunk.depth_mapsfirst. Build Depth Maps skips cameras with insufficient overlap; in a typical UAV project, a handful of cameras at the edges of the flight plan will have no depth map.chunk.transform.scaleisNonefor unreferenced chunks. Useor 1.0to fall back to a unit scale.- The
.files/<chunk>/<frame>/depth_maps/EXR files are unscaled. If you bypass Python and read them directly, remember to multiply bychunk.transform.scale. Thechunk.depth_maps[camera].image() * scaleform does the multiplication for you. Camera.Type.Regularfilters out keyframes / auxiliary frames. For specialised pipelines (laser scan trajectories etc.) you may want to handleCamera.Type.Keyframedifferently.- The output
.tiffiles require a viewer that handles 32-bit float. ImageJ, GDAL, OpenCV'sIMREAD_UNCHANGED, and thetifffilePython module all work; the macOS Preview application and most casual viewers will display a black or garbled image because they expect 8-bit.
Runnable demonstration on the Aerial-with-GCPs sample dataset¶
The script below exports float32 depth maps from an aligned chunk that has had Build Depth Maps run on it. Use the Aerial-with-GCPs sample.
Demo verified: ✗ — pending Tier 3 reproduction on Metashape Pro 2.2 / 2.3 with the Aerial-with-GCPs sample dataset. The underlying APIs are introspection-verified but the demo as written has not been run end-to-end. Required before the manual ships.
"""Export float32 depth maps from the Aerial-with-GCPs sample dataset.
Workflow: align the dataset in the GUI, run *Workflow → Build
Depth Maps* (any quality), then run this script in *Tools → Run
Script…*. The script prints how many depth maps were exported and
where; open one of the resulting .tif files in ImageJ or
`tifffile.imread` to confirm float32 content.
"""
import os
import Metashape
OUTPUT_DIR = "/path/to/depth_export/" # ← adjust
chunk = Metashape.app.document.chunk
if chunk is None:
raise SystemExit("Open the Aerial-with-GCPs chunk first.")
if not chunk.depth_maps:
raise SystemExit("No depth maps in this chunk — run *Build Depth Maps* first.")
os.makedirs(OUTPUT_DIR, exist_ok=True)
scale = chunk.transform.scale or 1.0
count = 0
for camera in chunk.cameras:
if camera.transform is None:
continue
if camera.type != Metashape.Camera.Type.Regular:
continue
if camera not in chunk.depth_maps:
continue
depth = chunk.depth_maps[camera].image() * scale
depth.save(os.path.join(OUTPUT_DIR, f"{camera.label}.tif"))
count += 1
print(f"exported {count} depth map(s) to {OUTPUT_DIR}")
print(f"chunk.transform.scale: {scale} (1.0 = unreferenced; otherwise metres-per-unit)")
Expected: one <camera_label>.tif per camera that has a depth map.
Inspect any of them with tifffile.imread() (or ImageJ) and
confirm the values are float32 in metres — for the Aerial-with-GCPs
project that is georeferenced in EPSG:4978 / a UTM zone, the depth
range typically falls between 30 m and 200 m above ground level.
If the file viewer reports 8-bit content, you ran the wrong tool — double-check that the script (not File → Export → Export Depth) produced the file.
References¶
- Official manual: Metashape Pro User Manual, ch. 3 "General workflow" → "Building point cloud" → Build Depth Maps — describes the depth-map step but not the Python export pattern.
- Python Reference:
Metashape.Chunk.depth_maps(theDepthMapscollection indexed by camera),Metashape.DepthMap.image()(returnsMetashape.Image),Metashape.Image.save(path),Metashape.Chunk.transform.scale,Metashape.Camera.Type.Regular— Metashape Python API Reference, version 2.3.1. - Forum: Pasumansky, 2020-09-11, Metashape 1.6 — full PySide2 dialog with the three-format export.
- Suggested sample dataset: Aerial images (with GCPs).
Has 444 cameras and a real CRS so
chunk.transform.scaleproduces metres.