Forum

Author Topic: Exporting depth maps  (Read 1353 times)

pbourke

  • Jr. Member
  • **
  • Posts: 55
    • View Profile
    • Personal/professional web site
Exporting depth maps
« 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.
Photographic reconstruction portfolio
http://paulbourke.net/reconstruction/portfolio/

pbourke

  • Jr. Member
  • **
  • Posts: 55
    • View Profile
    • Personal/professional web site
Depth maps
« Reply #1 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.
Photographic reconstruction portfolio
http://paulbourke.net/reconstruction/portfolio/

Arie

  • Full Member
  • ***
  • Posts: 126
    • View Profile
Re: Depth maps
« Reply #2 on: September 10, 2020, 12:46:13 PM »
Hi Paul,
take a look in the project folder (*.files\1\0\depth_maps\depth_maps.zip). 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.
Cheers!

Alexey Pasumansky

  • Agisoft Technical Support
  • Hero Member
  • *****
  • Posts: 13120
    • View Profile
Re: Depth maps
« Reply #3 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:
https://www.agisoft.com/forum/index.php?topic=12392.msg55345#msg55345
Best regards,
Alexey Pasumansky,
Agisoft LLC

Arie

  • Full Member
  • ***
  • Posts: 126
    • View Profile
Re: Depth maps
« Reply #4 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?

Alexey Pasumansky

  • Agisoft Technical Support
  • Hero Member
  • *****
  • Posts: 13120
    • View Profile
Re: Depth maps
« Reply #5 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

try:
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.pBar.setTextVisible(False)

#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.radioBtn_all.setChecked(True)
self.radioBtn_sel.setChecked(False)

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.setSpacing(10)
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)
self.setLayout(layout) 

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()"))

self.exec()


def export_depth(self):

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

app = QtWidgets.QApplication.instance()
global doc
doc = Metashape.app.document
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
else:
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):
camera_list.append(camera)
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 = Metashape.app.getExistingDirectory("Specify the export folder:")
if not output_folder:
print("Script aborted: invalid output folder.")
return 0

print("Script started...")
app.processEvents()
if chunk.transform.scale:
scale = chunk.transform.scale
else:
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")
else:
img = depth * scale
img.save(output_folder + "/" + camera.label + ".tif")
print("Processed depth for " + camera.label)
count += 1
self.pBar.setValue(int(count / len(camera_list) * 100))
app.processEvents()
self.pBar.setValue(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)

Metashape.app.addMenuItem("Custom menu/Export Depth Maps", export_depth_maps)
Best regards,
Alexey Pasumansky,
Agisoft LLC