Agisoft Metashape

Agisoft Metashape => General => Topic started by: pbourke on September 10, 2020, 07:45:01 AM

Title: Exporting depth maps
Post by: pbourke on September 10, 2020, 07:45:01 AM
Why, after creating a dense point cloud, can't I export the depth maps? They exist at that point. The menu is only active after I create a mesh, a step I should not need to do in order to get the depth maps.
Title: Depth maps
Post by: pbourke on September 10, 2020, 10:11:50 AM
Relatively surprised the depth maps, as exported, are only 8 bit.
Is that correct, are they only 8 bit internally also? Seems highly quantised if so.
Title: Re: Depth maps
Post by: Arie on September 10, 2020, 12:46:13 PM
Hi Paul,
take a look in the project folder (*.files\1\0\depth_maps\ There you have the depth-maps in EXR format in 32-bit.
If my memory is correct, you will have to check "Keep depth maps" under the advanced settings.
Title: Re: Depth maps
Post by: Alexey Pasumansky on September 10, 2020, 05:03:46 PM
Hello Paul,

If you are using Export Depth option which calculates the depth based on available mesh model, then the result is a grayscale RGB (8-bit).

For the 32-bit depth maps export of existing maps you should use Python scripting. As well as if you require to make a depth render of the current mesh, see the example of the latter in another thread:
Title: Re: Depth maps
Post by: Arie on September 10, 2020, 05:19:28 PM
Hi Alexey,
what's the difference between the export with Python scripting and the EXR images in the .files folder?
Is the approach with python scripting based on the existing mesh, whereas the images in .files folder are the actual basis for generating the mesh?
Title: Re: Depth maps
Post by: Alexey Pasumansky on September 11, 2020, 09:09:42 PM
Hello Arie,

The internal EXR maps in the project files structure are not scaled, so it's necessary to apply some scaling (also via Python). The script that I have referenced in the previous reply is rendering depth from existing model.

The following script that might be already published in some older threads allows to save the scaled depth maps from the project without rendering from the mesh, providing that the Depth Maps appear in the chunks contents of the Workspace pane (note that numpy module should be installed to Metashape Python first):
Code: [Select]

import Metashape
from PySide2 import QtGui, QtCore, QtWidgets

global NUMPY
NUMPY = True

import numpy
except ImportError:
NUMPY = False
print("numpy module not installed!")

class ExportDepthDlg(QtWidgets.QDialog):

def __init__ (self, parent):
QtWidgets.QDialog.__init__(self, parent)
self.setWindowTitle("Export depth maps")

self.btnQuit = QtWidgets.QPushButton("&Close")
self.btnP1 = QtWidgets.QPushButton("&Export")
self.pBar = QtWidgets.QProgressBar()

#self.selTxt =QtWidgets.QLabel()
#self.selTxt.setText("Apply to:")
self.radioBtn_all = QtWidgets.QRadioButton("Apply to all cameras")
self.radioBtn_sel = QtWidgets.QRadioButton("Apply to selected")

self.formTxt = QtWidgets.QLabel()
self.formTxt.setText("Export format:")
self.formCmb = QtWidgets.QComboBox()
self.formCmb.addItem("1-band F32")
self.formCmb.addItem("Grayscale 8-bit")
self.formCmb.addItem("Grayscale 16-bit")

layout = QtWidgets.QGridLayout()   #creating layout
layout.addWidget(self.radioBtn_all, 0, 1)
layout.addWidget(self.radioBtn_sel, 1, 1)
layout.addWidget(self.formTxt, 0, 2)
layout.addWidget(self.formCmb, 1, 2)
layout.addWidget(self.pBar, 2, 0)
layout.addWidget(self.btnP1, 2, 1)
layout.addWidget(self.btnQuit, 2, 2)

proc_depth = lambda : self.export_depth()
QtCore.QObject.connect(self.btnP1, QtCore.SIGNAL("clicked()"), proc_depth)
QtCore.QObject.connect(self.btnQuit, QtCore.SIGNAL("clicked()"), self, QtCore.SLOT("reject()"))


def export_depth(self):

global NUMPY
if not NUMPY:"Script aborted: numpy module is not installed.")
return 0

app = QtWidgets.QApplication.instance()
global doc
doc =
chunk = doc.chunk #active chunk

if self.formCmb.currentText() == "1-band F32":
F32 = True
elif self.formCmb.currentText() == "Grayscale 8-bit":
F32 = False
elif self.formCmb.currentText() == "Grayscale 16-bit":
F32 = False
print("Script aborted: unexpected error.")
return 0

selected = False
camera_list = list()
if self.radioBtn_sel.isChecked():
selected = True
for camera in chunk.cameras:
if camera.selected and camera.transform and (camera.type == Metashape.Camera.Type.Regular):
elif self.radioBtn_all.isChecked():
selected = False
camera_list = [camera for camera in chunk.cameras if (camera.transform and camera.type == Metashape.Camera.Type.Regular)]

if not len(camera_list):
print("Script aborted: nothing to export.")
return 0

output_folder ="Specify the export folder:")
if not output_folder:
print("Script aborted: invalid output folder.")
return 0

print("Script started...")
if chunk.transform.scale:
scale = chunk.transform.scale
scale = 1
count = 0

for camera in camera_list:
if camera in chunk.depth_maps.keys():
depth = chunk.depth_maps[camera].image()
if not F32:
img = numpy.frombuffer(depth.tostring(), dtype=numpy.float32)
depth_range = img.max() - img.min()
img = depth - img.min()
img = img * (1. / depth_range)
if self.formCmb.currentText() == "Grayscale 8-bit":
img = img.convert("RGB", "U8")
img = 255 - img
img = img - 255 * (img * (1 / 255))#normalized
img = img.convert("RGB", "U8")
elif self.formCmb.currentText() == "Grayscale 16-bit":
img = img.convert("RGB", "U16")
img = 65535 - img
img = img - 65535 * (img * (1 / 65535))#normalized
img = img.convert("RGB", "U16")
img = depth * scale + "/" + camera.label + ".tif")
print("Processed depth for " + camera.label)
count += 1
self.pBar.setValue(int(count / len(camera_list) * 100))
print("Script finished. Total cameras processed: " + str(count))
print("Depth maps exported to:\n " + output_folder)
return 1

def export_depth_maps():

app = QtWidgets.QApplication.instance()
parent = app.activeWindow()
dlg = ExportDepthDlg(parent)"Custom menu/Export Depth Maps", export_depth_maps)