Agisoft Metashape
Agisoft Metashape => Python and Java API => Topic started by: Andrew on April 13, 2014, 09:30:33 PM
-
I use Photoscan Standard edition - looking at feature list (considering my needs) I just couldn't find justification for upgrade. When looking at "Python scripting support" line I assumed it meant basically a more advanced and open alternative to "Batch Process" feature, but every now and then I read it can do lots more, hence my question: are any useful scripts included in Pro demo installation package? Or maybe there is some repository of useful scripts available? Or at least a list of what some more advanced scripts can accomplish? I think more insight into this can help push me and many other over the edge to upgrade.
-Andrew
-
That would indeed be nice.
We got two scripts from Agisoft directly, one removes duplicate cameras and the other splits up a project into a grid of chunks (for meshing Dense Clouds with a huge amount of points). Both are pretty simple, but big timesavers.
It would be great to have a repository of scripts, it might even spark some more development from us users as well.
-
Hello Andrew,
Currently there's no script repository or collection available.
Most of scripts that we have were created for special tasks (by user requests or for our internal needs), so they require description that could be even longer than the script itself. Another reason why we do not put all the scripts online, the need to keep them up-to-date. Sometimes PhotoScan Python API changes and older scripts need to be modified according to such changes, but keeping the entire collection updates is not reasonable, since most of scripts will not be used due to their specifics.
But still we have Python scripting sub-forum here and will surely help any PhotoScan user that require some assistance in script creation.
-
I I could imagine that a solution like the plug ins in qgis with an online repository would be grat and I could imagine this would be a good place to share and improve on existing stuff so users could update the scripts if needed if they are able to do so which iam am not honestly ...
but I wold think to have a kind of central registry at least would be good so people could see whats possible using ps pro
yours
frank
-
Hello Frank,
We've added Python scripting section to our Wiki: http://wiki.agisoft.com/wiki/Python
So everyone is free to post there his own script, also we'll try to publish there some general scripts that might be useful.
-
NICE, thanks Alexey!
-
Wouldn't a GIT style environment make more sense? Better collaboration etc.
-
The script, PS110_split_in_chunks_dialog.py, leaves a seam between chunks in an ortho created after merged back.
-
Hello Bill,
It is possible that there's a one pixel wide gap between merged chunks.
The problem can be avoided if region.size is increased, for example, by 1%.
-
Thanks Alexey, I'll give it a try.
-
What are the variables for changing dense cloud quality?
-
Couldn't find where to download a manual on python on the Agisoft web site, but this turned up on a Google search:
downloads.agisoft.ru/pdf/photoscan_python_api_1_0_0.pdf
-
Hello Bill,
Python API reference can be found here:
http://www.agisoft.com/downloads/user-manuals/
The options for the dense cloud quality are the following:
PhotoScan.Quality.UltraQuality
PhotoScan.Quality.HighQuality
PhotoScan.Quality.MediumQuality
PhotoScan.Quality.LowQuality
PhotoScan.Quality.LowestQuality
-
Wouldn't a GIT style environment make more sense? Better collaboration etc.
I'd like to second this suggestion. A GitHub repository where people could submit pull requests with new scripts or changes would be great.
-
If there is any repository with PhotoScan scripts driven by community, we can put a link to it here and in wiki pages.
-
Hello Bill,
Python API reference can be found here:
http://www.agisoft.com/downloads/user-manuals/
The options for the dense cloud quality are the following:
PhotoScan.Quality.UltraQuality
PhotoScan.Quality.HighQuality
PhotoScan.Quality.MediumQuality
PhotoScan.Quality.LowQuality
PhotoScan.Quality.LowestQuality
Thanks Alexey
-
I'm getting the following error when I run Split in chunks.py:
2016-01-25 06:24:47 Traceback (most recent call last):
2016-01-25 06:24:47 File "E:/Photoscan/scripts/Split in chunks.py", line 80, in <lambda>
2016-01-25 06:24:47 proc_split = lambda : self.splitChunks()
2016-01-25 06:24:47 File "E:/Photoscan/scripts/Split in chunks.py", line 159, in splitChunks
2016-01-25 06:24:47 doc.addChunk(new_chunk)
2016-01-25 06:24:47 TypeError: addChunk() takes no arguments (1 given)
>>>
PhotoScan version 1.2.3
-
Hello Bill,
You need to comment that line - doc.addChunk(new_chunk), as chunk.copy() operation automatically creates chunk duplicate in the document.
-
Dear Alexey
Can you please poste the actual "split in chuncs"-Script. for me it does not work with comment that line..:-/
Thank you
-
Hello ristag,
Which version of PhotoScan you are using? If you are no 1.2, then it should work without commenting or uncommenting anything.
-
Hello Bill,
Python API reference can be found here:
http://www.agisoft.com/downloads/user-manuals/
The options for the dense cloud quality are the following:
PhotoScan.Quality.UltraQuality
PhotoScan.Quality.HighQuality
PhotoScan.Quality.MediumQuality
PhotoScan.Quality.LowQuality
PhotoScan.Quality.LowestQuality
http://wiki.agisoft.com/wiki/Split_in_chunks.py (http://wiki.agisoft.com/wiki/Split_in_chunks.py)
Also handy variables for modifying the Split_in_chunks script:
PhotoScan.FilterMode.AggressiveFiltering
PhotoScan.FilterMode.ModerateFiltering
PhotoScan.FilterMode.MildFiltering
PhotoScan.FilterMode.NoFiltering
-
Error
-
Hi everyone,
Hope this helps:
http://www.agisoft.com/forum/index.php?topic=5536
http://www.agisoft.com/forum/index.php?topic=5530
-
Is there still interest in a repository of scripts? I'd be willing to maintain it. I set up a repo here: https://github.com/FoxRow/Agisoft-Scripting (https://github.com/FoxRow/Agisoft-Scripting)
I can pull in the scripts already in http://wiki.agisoft.com/wiki/Python. If we get a critical mass, it would be nice to release it as a companion package on PyPI. I don't see any license on the scripts already in the wiki, does maybe someone from Agisoft know their provenance? I'd like to have a coherent license for the repository in that case.
-
Hello all,
We have moved the scripts from Wiki to GitHub repository, so that it should be easier to contribute for the community:
https://github.com/agisoft-llc/photoscan-scripts
-
Good to see that it has been moved to GitHub.
;D
-
Now it should be easier for community to contribute.
We are also trying to update the collection with the commonly requested scripts.
-
please can anyone be kind enough to assist me debug this script
# This is python script for Metashape Pro. Scripts repository: https://github.com/agisoft-llc/metashape-scripts
import Metashape
from PySide2 import QtGui, QtCore, QtWidgets
# Checking compatibility
compatible_major_version = "1.5"
found_major_version = ".".join(Metashape.app.version.split('.')[:2])
if found_major_version != compatible_major_version:
raise Exception("Incompatible Metashape version: {} != {}".format(found_major_version, compatible_major_version))
QUALITY = {"1": Metashape.UltraQuality,
"2": Metashape.HighQuality,
"4": Metashape.MediumQuality,
"8": Metashape.LowQuality,
"16": Metashape.LowestQuality}
FILTERING = {"3": Metashape.NoFiltering,
"0": Metashape.MildFiltering,
"1": Metashape.ModerateFiltering,
"2": Metashape.AggressiveFiltering}
MESH = {"Arbitrary": Metashape.SurfaceType.Arbitrary,
"Height Field": Metashape.SurfaceType.HeightField}
DENSE = {"Ultra": Metashape.UltraQuality,
"High": Metashape.HighQuality,
"Medium": Metashape.MediumQuality,
"Low": Metashape.LowQuality,
"Lowest": Metashape.LowestQuality}
def isIdent(matrix):
"""
Check if the matrix is identity matrix
"""
for i in range(matrix.size[0]):
for j in range(matrix.size[1]):
if i == j:
if matrix[i, j] != 1.0:
return False
elif matrix[i, j]:
return False
return True
class SplitDlg(QtWidgets.QDialog):
def __init__(self, parent):
QtWidgets.QDialog.__init__(self, parent)
self.setWindowTitle("Split in chunks")
self.gridX = 2
self.gridY = 2
self.gridWidth = 198
self.gridHeight = 198
self.spinX = QtWidgets.QSpinBox()
self.spinX.setMinimum(1)
self.spinX.setValue(2)
self.spinX.setMaximum(20)
self.spinX.setFixedSize(75, 25)
self.spinY = QtWidgets.QSpinBox()
self.spinY.setMinimum(1)
self.spinY.setValue(2)
self.spinY.setMaximum(20)
self.spinY.setFixedSize(75, 25)
self.chkMesh = QtWidgets.QCheckBox("Build Mesh")
self.chkMesh.setFixedSize(100, 50)
self.chkMesh.setToolTip("Generates mesh for each cell in grid")
self.meshBox = QtWidgets.QComboBox()
for element in MESH.keys():
self.meshBox.addItem(element)
self.meshBox.setFixedSize(100, 25)
self.chkDense = QtWidgets.QCheckBox("Build Dense Cloud")
self.chkDense.setFixedSize(120, 50)
self.chkDense.setWhatsThis("Builds dense cloud for each cell in grid")
self.denseBox = QtWidgets.QComboBox()
for element in DENSE.keys():
self.denseBox.addItem(element)
self.denseBox.setFixedSize(100, 25)
self.chkMerge = QtWidgets.QCheckBox("Merge Back")
self.chkMerge.setFixedSize(90, 50)
self.chkMerge.setToolTip("Merges back the processing products formed in the individual cells")
self.chkSave = QtWidgets.QCheckBox("Autosave")
self.chkSave.setFixedSize(90, 50)
self.chkSave.setToolTip("Autosaves the project after each operation")
self.txtOvp = QtWidgets.QLabel()
self.txtOvp.setText("Overlap (%):")
self.txtOvp.setFixedSize(90, 25)
self.edtOvp = QtWidgets.QLineEdit()
self.edtOvp.setPlaceholderText("0")
self.edtOvp.setFixedSize(50, 25)
self.btnQuit = QtWidgets.QPushButton("Close")
self.btnQuit.setFixedSize(90, 50)
self.btnP1 = QtWidgets.QPushButton("Split")
self.btnP1.setFixedSize(90, 50)
self.grid = QtWidgets.QLabel(" ")
self.grid.resize(self.gridWidth, self.gridHeight)
tempPixmap = QtGui.QPixmap(self.gridWidth, self.gridHeight)
tempImage = tempPixmap.toImage()
for y in range(self.gridHeight):
for x in range(self.gridWidth):
if not (x and y) or (x == self.gridWidth - 1) or (y == self.gridHeight - 1):
tempImage.setPixel(x, y, QtGui.qRgb(0, 0, 0))
elif (x == self.gridWidth / 2) or (y == self.gridHeight / 2):
tempImage.setPixel(x, y, QtGui.qRgb(0, 0, 0))
else:
tempImage.setPixel(x, y, QtGui.qRgb(255, 255, 255))
tempPixmap = tempPixmap.fromImage(tempImage)
self.grid.setPixmap(tempPixmap)
self.grid.show()
layout = QtWidgets.QGridLayout() # creating layout
layout.addWidget(self.spinX, 1, 0)
layout.addWidget(self.spinY, 1, 1, QtCore.Qt.AlignRight)
layout.addWidget(self.chkDense, 0, 2)
layout.addWidget(self.chkMesh, 0, 3)
layout.addWidget(self.chkMerge, 0, 4)
layout.addWidget(self.meshBox, 1, 3, QtCore.Qt.AlignTop)
layout.addWidget(self.denseBox, 1, 2, QtCore.Qt.AlignTop)
layout.addWidget(self.chkSave, 3, 2)
layout.addWidget(self.btnP1, 3, 3)
layout.addWidget(self.btnQuit, 3, 4)
layout.addWidget(self.txtOvp, 0, 0, QtCore.Qt.AlignRight)
layout.addWidget(self.edtOvp, 0, 1, QtCore.Qt.AlignLeft)
layout.addWidget(self.grid, 2, 0, 2, 2)
# layout.setAlignment(QtCore.Qt.AlignTop)
self.setLayout(layout)
proc_split = lambda: self.splitChunks()
self.spinX.valueChanged.connect(self.updateGrid)
self.spinY.valueChanged.connect(self.updateGrid)
QtCore.QObject.connect(self.btnP1, QtCore.SIGNAL("clicked()"), proc_split)
QtCore.QObject.connect(self.btnQuit, QtCore.SIGNAL("clicked()"), self, QtCore.SLOT("reject()"))
self.exec()
def updateGrid(self):
"""
Draw new grid
"""
self.gridX = self.spinX.value()
self.gridY = self.spinY.value()
tempPixmap = QtGui.QPixmap(self.gridWidth, self.gridHeight)
tempImage = tempPixmap.toImage()
tempImage.fill(QtGui.qRgb(240, 240, 240))
for y in range(int(self.gridHeight / self.gridY) * self.gridY):
for x in range(int(self.gridWidth / self.gridX) * self.gridX):
if not (x and y) or (x == self.gridWidth - 1) or (y == self.gridHeight - 1):
tempImage.setPixel(x, y, QtGui.qRgb(0, 0, 0))
elif y > int(self.gridHeight / self.gridY) * self.gridY:
tempImage.setPixel(x, y, QtGui.qRgb(240, 240, 240))
elif x > int(self.gridWidth / self.gridX) * self.gridX:
tempImage.setPixel(x, y, QtGui.qRgb(240, 240, 240))
else:
tempImage.setPixel(x, y, QtGui.qRgb(255, 255, 255))
for y in range(0, int(self.gridHeight / self.gridY + 1) * self.gridY, int(self.gridHeight / self.gridY)):
for x in range(int(self.gridWidth / self.gridX) * self.gridX):
tempImage.setPixel(x, y, QtGui.qRgb(0, 0, 0))
for x in range(0, int(self.gridWidth / self.gridX + 1) * self.gridX, int(self.gridWidth / self.gridX)):
for y in range(int(self.gridHeight / self.gridY) * self.gridY):
tempImage.setPixel(x, y, QtGui.qRgb(0, 0, 0))
tempPixmap = tempPixmap.fromImage(tempImage)
self.grid.setPixmap(tempPixmap)
self.grid.show()
return True
def splitChunks(self):
self.gridX = self.spinX.value()
self.gridY = self.spinY.value()
partsX = self.gridX
partsY = self.gridY
print("Script started...")
buildMesh = self.chkMesh.isChecked()
buildDense = self.chkDense.isChecked()
mergeBack = self.chkMerge.isChecked()
autosave = self.chkSave.isChecked()
quality = DENSE[self.denseBox.currentText()]
mesh_mode = MESH[self.meshBox.currentText()]
doc = Metashape.app.document
chunk = doc.chunk
if not chunk.transform.translation.norm():
chunk.transform.matrix = chunk.transform.matrix
elif chunk.transform.scale == 1:
chunk.transform.matrix = chunk.transform.matrix
elif isIdent(chunk.transform.rotation):
chunk.transform.matrix = chunk.transform.matrix
region = chunk.region
r_center = region.center
r_rotate = region.rot
r_size = region.size
x_scale = r_size.x / partsX
y_scale = r_size.y / partsY
z_scale = r_size.z
offset = r_center - r_rotate * r_size / 2.
chunk_labels = [ichunk.label for ichunk in PhotoScan.app.document.chunks]
if "Chunk " + str(i) + "_" + str(j) in chunk_labels:
continue
for j in range(1, partsY + 1): # creating new chunks and adjusting bounding box
for i in range(1, partsX + 1):
if not buildDense:
new_chunk = chunk.copy(items=[Metashape.DataSource.DenseCloudData, Metashape.DataSource.DepthMapsData])
else:
new_chunk = chunk.copy(items=[])
new_chunk.label = "Chunk " + str(i) + "_" + str(j)
if new_chunk.model:
new_chunk.model.clear()
new_region = Metashape.Region()
new_rot = r_rotate
new_center = Metashape.Vector([(i - 0.5) * x_scale, (j - 0.5) * y_scale, 0.5 * z_scale])
new_center = offset + new_rot * new_center
new_size = Metashape.Vector([x_scale, y_scale, z_scale])
if self.edtOvp.text().isdigit():
new_region.size = new_size * (1 + float(self.edtOvp.text()) / 100)
else:
new_region.size = new_size
new_region.center = new_center
new_region.rot = new_rot
new_chunk.region = new_region
Metashape.app.update()
if autosave:
doc.save()
if buildDense:
if new_chunk.depth_maps:
reuse_depth = True
if new_chunk.depth_maps.meta['depth/depth_downscale']:
quality = QUALITY[new_chunk.depth_maps.meta['depth/depth_downscale']]
if new_chunk.depth_maps.meta['depth/depth_filter_mode']:
filtering = FILTERING[new_chunk.depth_maps.meta['depth/depth_filter_mode']]
try:
new_chunk.buildDepthMaps(quality=quality, filter=filtering, reuse_depth=reuse_depth)
new_chunk.buildDenseCloud(max_neighbors=100) # keep_depth=False
except RuntimeError:
print("Can't build dense cloud for " + chunk.label)
else:
reuse_depth = False
try:
new_chunk.buildDepthMaps(quality=quality,
filter=Metashape.FilterMode.ModerateFiltering, reuse_depth=reuse_depth)
new_chunk.buildDenseCloud(max_neighbors=100) # keep_depth=False
except RuntimeError:
print("Can't build dense cloud for " + chunk.label)
if autosave:
doc.save()
if buildMesh:
if new_chunk.dense_cloud:
try:
new_chunk.buildModel(surface=mesh_mode,
source=Metashape.DataSource.DenseCloudData,
interpolation=Metashape.Interpolation.EnabledInterpolation,
face_count=Metashape.FaceCount.HighFaceCount)
except RuntimeError:
print("Can't build mesh for " + chunk.label)
else:
try:
new_chunk.buildModel(surface=mesh_mode,
source=Metashape.DataSource.PointCloudData,
interpolation=Metashape.Interpolation.EnabledInterpolation,
face_count=Metashape.FaceCount.HighFaceCount)
except RuntimeError:
print("Can't build mesh for " + chunk.label)
if autosave:
doc.save()
if not buildDense:
if new_chunk.dense_cloud:
new_chunk.dense_cloud.clear()
if new_chunk.depth_maps:
new_chunk.depth_maps.clear()
# new_chunk = None
if mergeBack:
for i in range(1, len(doc.chunks)):
chunk = doc.chunks
chunk.remove(chunk.cameras)
doc.chunks[0].model = None # hiding the mesh of the original chunk, just for case
doc.mergeChunks(doc.chunks,
merge_dense_clouds=True, merge_models=True, merge_markers=True) # merging all smaller chunks into single one
doc.remove(doc.chunks[1:-1]) # removing smaller chunks.
if autosave:
doc.save()
if autosave:
doc.save()
print("Script finished!")
return True
def split_in_chunks():
global doc
doc = Metashape.app.documenthttps://www.sublimetext.com/2
app = QtWidgets.QApplication.instance()
parent = app.activeWindow()
dlg = SplitDlg(parent)
label = "Custom menu/Split in chunks"
Metashape.app.addMenuItem(label, split_in_chunks)
print("To execute this script press {}".format(label))
it is the split in chunk script with 3 added lines above line 237 to resume the operation after a power failure. I cannot seem to debug it. Please help!!!!
-
Python scripts for Metashape Pro
https://github.com/agisoft-llc/metashape-scripts
How to run Python script automatically on Metashape Professional start
https://agisoft.freshdesk.com/support/solutions/articles/31000133123-how-to-run-python-script-automatically-on-metashape-professional-start
Very useful :), thanks.
-
+1 great reasource!
-
Our partners from Geoscan have published quite a few scripts for Metashape which extends the functionality of the application (mostly related to aerial survey projects):
https://github.com/geoscan/geoscan_plugins
The list of available scripts can be found on the following page:
https://github.com/geoscan/geoscan_plugins#%D1%81%D0%BF%D0%B8%D1%81%D0%BE%D0%BA-%D0%BF%D0%BB%D0%B0%D0%B3%D0%B8%D0%BD%D0%BE%D0%B2
Not sure, if they are planning to add the description in English, but online translation tools should help, if you can't read in Russian.
-
So, i tried to install the script "detect_objects.py" from https://github.com/agisoft-llc/metashape-scripts/tree/master/src (https://github.com/agisoft-llc/metashape-scripts/tree/master/src) but i have a problem.
When i try to run the script i get the following error
Shape layer: key=0, label=Layer, enabled=False
2021-12-01 16:19:52 Shape layer: key=1, label=Train zones, enabled=True
2021-12-01 16:19:52 Shape layer: key=2, label=Train data, enabled=True
2021-12-01 16:19:52 Shape layer: key=3, label=Objects, enabled=True
2021-12-01 16:20:11 Shape layer: key=0, label=Layer, enabled=False
2021-12-01 16:20:11 Shape layer: key=1, label=Train zones, enabled=True
2021-12-01 16:20:11 Shape layer: key=2, label=Train data, enabled=True
2021-12-01 16:20:11 Shape layer: key=3, label=Objects, enabled=True
2021-12-01 16:20:32 Using resolution 0.020156805028629442 m/pix with patch 1600x1600
2021-12-01 16:20:32 Additional neural network training expected on key=2 layer data w.r.t. key=1 layer zones
2021-12-01 16:20:32 Loading train shapes...
2021-12-01 16:20:32 1 train zones and 12 train data loaded in 0.00 sec
2021-12-01 16:20:32 Working dir: E:\- PROJECTS -\01. ANAX Projects\10. DIAFORA\Ioannina - University\- Agisoft Projects\objects_detection
2021-12-01 16:20:37 Traceback (most recent call last):
2021-12-01 16:20:37 File "C:/Users/ANAX-UAS/AppData/Local/Agisoft/Photogrammetric Kit for TOPCON/scripts/detect_objects.py", line 1139, in <lambda>
2021-12-01 16:20:37 QtCore.QObject.connect(self.btnRun, QtCore.SIGNAL("clicked()"), lambda: self.process())
2021-12-01 16:20:37 File "C:/Users/ANAX-UAS/AppData/Local/Agisoft/Photogrammetric Kit for TOPCON/scripts/detect_objects.py", line 154, in process
2021-12-01 16:20:37 self.prepair()
2021-12-01 16:20:37 File "C:/Users/ANAX-UAS/AppData/Local/Agisoft/Photogrammetric Kit for TOPCON/scripts/detect_objects.py", line 221, in prepair
2021-12-01 16:20:37 from deepforest import utilities
2021-12-01 16:20:37 File "C:\Program Files\Agisoft\Photogrammetric Kit Pro\python\Lib\site-packages\shiboken2\files.dir\shibokensupport\__feature__.py", line 114, in _import
2021-12-01 16:20:37 return original_import(name, *args, **kwargs)
2021-12-01 16:20:37 File "C:\Program Files\Agisoft\Photogrammetric Kit Pro\python\lib\site-packages\deepforest\utilities.py", line 10, in <module>
2021-12-01 16:20:37 import rasterio
2021-12-01 16:20:37 File "C:\Program Files\Agisoft\Photogrammetric Kit Pro\python\Lib\site-packages\shiboken2\files.dir\shibokensupport\__feature__.py", line 114, in _import
2021-12-01 16:20:37 return original_import(name, *args, **kwargs)
2021-12-01 16:20:37 File "C:\Program Files\Agisoft\Photogrammetric Kit Pro\python\lib\site-packages\rasterio\__init__.py", line 17, in <module>
2021-12-01 16:20:37 from rasterio._base import gdal_version
2021-12-01 16:20:37 File "C:\Program Files\Agisoft\Photogrammetric Kit Pro\python\Lib\site-packages\shiboken2\files.dir\shibokensupport\__feature__.py", line 114, in _import
2021-12-01 16:20:37 return original_import(name, *args, **kwargs)
2021-12-01 16:20:37 ImportError: DLL load failed while importing _base: The specified module could not be found.
I do not what is wrong and why it does not work.
Anyone installed that script and got it working?