Difference between revisions of "Split in chunks.py"
From Agisoft
(Created page with "<pre> #adds custom menu item #allows to split the original chunk into multiple chunks with smaller bounding boxes forming a grid #building dense cloud, mesh and merging the re...") |
|||
(4 intermediate revisions by the same user not shown) | |||
Line 1: | Line 1: | ||
<pre> | <pre> | ||
− | # | + | #compatibility PhotoScan Pro 1.3 |
− | #allows to split the original chunk into multiple chunks with smaller bounding boxes forming a grid | + | |
+ | #allows to split the original chunk into multiple chunks with smaller bounding boxes forming a user-defined grid | ||
#building dense cloud, mesh and merging the result back is optional | #building dense cloud, mesh and merging the result back is optional | ||
import PhotoScan | import PhotoScan | ||
− | from | + | from PySide2 import QtGui, QtCore, QtWidgets |
+ | |||
+ | QUALITY = {"1":PhotoScan.UltraQuality, | ||
+ | "2":PhotoScan.HighQuality, | ||
+ | "4":PhotoScan.MediumQuality, | ||
+ | "8":PhotoScan.LowQuality, | ||
+ | "16":PhotoScan.LowestQuality} | ||
+ | |||
+ | FILTERING = {"3":PhotoScan.NoFiltering, | ||
+ | "0":PhotoScan.MildFiltering, | ||
+ | "1":PhotoScan.ModerateFiltering, | ||
+ | "2":PhotoScan.AggressiveFiltering} | ||
− | class SplitDlg( | + | class SplitDlg(QtWidgets.QDialog): |
def __init__(self, parent): | def __init__(self, parent): | ||
− | + | QtWidgets.QDialog.__init__(self, parent) | |
self.setWindowTitle("Split in chunks") | self.setWindowTitle("Split in chunks") | ||
Line 19: | Line 31: | ||
self.gridHeight = 198 | self.gridHeight = 198 | ||
− | self.spinX = | + | self.spinX = QtWidgets.QSpinBox() |
self.spinX.setMinimum(2) | self.spinX.setMinimum(2) | ||
self.spinX.setMaximum(20) | self.spinX.setMaximum(20) | ||
self.spinX.setFixedSize(75, 25) | self.spinX.setFixedSize(75, 25) | ||
− | self.spinY = | + | self.spinY = QtWidgets.QSpinBox() |
self.spinY.setMinimum(2) | self.spinY.setMinimum(2) | ||
self.spinY.setMaximum(20) | self.spinY.setMaximum(20) | ||
self.spinY.setFixedSize(75, 25) | self.spinY.setFixedSize(75, 25) | ||
− | self.chkMesh = | + | self.chkMesh = QtWidgets.QCheckBox("Build Mesh") |
− | self.chkMesh.setFixedSize( | + | self.chkMesh.setFixedSize(100,50) |
self.chkMesh.setToolTip("Generates mesh for each cell in grid") | self.chkMesh.setToolTip("Generates mesh for each cell in grid") | ||
− | self.chkDense = | + | self.chkDense = QtWidgets.QCheckBox("Build Dense Cloud") |
− | self.chkDense.setFixedSize( | + | self.chkDense.setFixedSize(120,50) |
self.chkDense.setWhatsThis("Builds dense cloud for each cell in grid") | self.chkDense.setWhatsThis("Builds dense cloud for each cell in grid") | ||
− | self.chkMerge = | + | self.chkMerge = QtWidgets.QCheckBox("Merge Back") |
self.chkMerge.setFixedSize(90,50) | self.chkMerge.setFixedSize(90,50) | ||
self.chkMerge.setToolTip("Merges back the processing products formed in the individual cells") | 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.btnQuit = | + | self.txtOvp = QtWidgets.QLabel() |
+ | self.txtOvp.setText("Overlap (%):") | ||
+ | self.txtOvp.setFixedSize(90, 25) | ||
+ | |||
+ | self.edtOvp = QtWidgets.QLineEdit() | ||
+ | self.edtOvp.setPlaceholderText("0") | ||
+ | self.edtOvp.setFixedSize(100, 25) | ||
+ | |||
+ | self.btnQuit = QtWidgets.QPushButton("Close") | ||
self.btnQuit.setFixedSize(90,50) | self.btnQuit.setFixedSize(90,50) | ||
− | self.btnP1 = | + | self.btnP1 = QtWidgets.QPushButton("Split") |
self.btnP1.setFixedSize(90,50) | self.btnP1.setFixedSize(90,50) | ||
− | self.grid = | + | self.grid = QtWidgets.QLabel(" ") |
self.grid.resize(self.gridWidth, self.gridHeight) | self.grid.resize(self.gridWidth, self.gridHeight) | ||
tempPixmap = QtGui.QPixmap(self.gridWidth, self.gridHeight) | tempPixmap = QtGui.QPixmap(self.gridWidth, self.gridHeight) | ||
Line 66: | Line 90: | ||
self.grid.show() | self.grid.show() | ||
− | layout = | + | layout = QtWidgets.QGridLayout() #creating layout |
layout.addWidget(self.spinX, 0, 0) | layout.addWidget(self.spinX, 0, 0) | ||
layout.addWidget(self.spinY, 0, 1) | layout.addWidget(self.spinY, 0, 1) | ||
Line 74: | Line 98: | ||
layout.addWidget(self.chkMerge, 0, 4) | layout.addWidget(self.chkMerge, 0, 4) | ||
− | layout.addWidget(self.btnP1, | + | layout.addWidget(self.btnP1, 3, 2) |
− | layout.addWidget(self.btnQuit, | + | layout.addWidget(self.btnQuit, 3, 3) |
+ | |||
+ | layout.addWidget(self.txtOvp, 1, 3) | ||
+ | layout.addWidget(self.edtOvp, 1, 4) | ||
+ | |||
+ | layout.addWidget(self.chkSave, 2, 4) | ||
+ | |||
layout.addWidget(self.grid, 1, 0, 2, 2) | layout.addWidget(self.grid, 1, 0, 2, 2) | ||
self.setLayout(layout) | self.setLayout(layout) | ||
Line 138: | Line 168: | ||
buildDense = self.chkDense.isChecked() | buildDense = self.chkDense.isChecked() | ||
mergeBack = self.chkMerge.isChecked() | mergeBack = self.chkMerge.isChecked() | ||
+ | autosave = self.chkSave.isChecked() | ||
doc = PhotoScan.app.document | doc = PhotoScan.app.document | ||
chunk = doc.chunk | chunk = doc.chunk | ||
+ | |||
+ | if not chunk.transform.translation: | ||
+ | chunk.transform.matrix = chunk.transform.matrix | ||
region = chunk.region | region = chunk.region | ||
Line 155: | Line 189: | ||
for j in range(1, partsY + 1): #creating new chunks and adjusting bounding box | for j in range(1, partsY + 1): #creating new chunks and adjusting bounding box | ||
for i in range(1, partsX + 1): | for i in range(1, partsX + 1): | ||
− | new_chunk = chunk.copy() | + | new_chunk = chunk.copy(items = [PhotoScan.DataSource.DenseCloudData]) |
new_chunk.label = "Chunk "+ str(i)+ "\\" + str(j) | new_chunk.label = "Chunk "+ str(i)+ "\\" + str(j) | ||
new_chunk.model = None | new_chunk.model = None | ||
− | + | ||
− | + | ||
new_region = PhotoScan.Region() | new_region = PhotoScan.Region() | ||
new_rot = r_rotate | new_rot = r_rotate | ||
Line 165: | Line 198: | ||
new_center = offset + new_rot * new_center | new_center = offset + new_rot * new_center | ||
new_size = PhotoScan.Vector([x_scale, y_scale, z_scale]) | new_size = PhotoScan.Vector([x_scale, y_scale, z_scale]) | ||
− | new_region.size = new_size | + | |
+ | 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.center = new_center | ||
new_region.rot = new_rot | new_region.rot = new_rot | ||
Line 172: | Line 210: | ||
PhotoScan.app.update() | PhotoScan.app.update() | ||
+ | |||
+ | if autosave: | ||
+ | doc.save() | ||
if buildDense: | if buildDense: | ||
− | new_chunk.buildDenseCloud(quality = PhotoScan.Quality. | + | if new_chunk.depth_maps: |
+ | reuse_depth = True | ||
+ | quality = QUALITY[new_chunk.depth_maps.meta['depth/depth_downscale']] | ||
+ | filtering = FILTERING[new_chunk.depth_maps.meta['depth/depth_filter_mode']] | ||
+ | try: | ||
+ | new_chunk.buildDenseCloud(quality = quality, filter = filtering, keep_depth = False, reuse_depth = reuse_depth) | ||
+ | except RuntimeError: | ||
+ | print("Can't build dense cloud for " + chunk.label) | ||
+ | |||
+ | else: | ||
+ | reuse_depth = False | ||
+ | try: | ||
+ | new_chunk.buildDenseCloud(quality = PhotoScan.Quality.HighQuality, filter = PhotoScan.FilterMode.AggressiveFiltering, keep_depth = False, reuse_depth = reuse_depth) | ||
+ | except RuntimeError: | ||
+ | print("Can't build dense cloud for " + chunk.label) | ||
+ | |||
+ | |||
+ | if autosave: | ||
+ | doc.save() | ||
if buildMesh: | if buildMesh: | ||
if new_chunk.dense_cloud: | if new_chunk.dense_cloud: | ||
− | new_chunk.buildModel(surface = PhotoScan.SurfaceType.HeightField, source = PhotoScan. | + | try: |
+ | new_chunk.buildModel(surface = PhotoScan.SurfaceType.HeightField, source = PhotoScan.DataSource.DenseCloudData, interpolation = PhotoScan.Interpolation.EnabledInterpolation, face_count = PhotoScan.FaceCount.HighFaceCount) | ||
+ | except RuntimeError: | ||
+ | print("Can't build mesh for " + chunk.label) | ||
else: | else: | ||
− | new_chunk.buildModel(surface = PhotoScan.SurfaceType.HeightField, source = PhotoScan. | + | try: |
+ | new_chunk.buildModel(surface = PhotoScan.SurfaceType.HeightField, source = PhotoScan.DataSource.PointCloudData, interpolation = PhotoScan.Interpolation.EnabledInterpolation, face_count = PhotoScan.FaceCount.HighFaceCount) | ||
+ | except RuntimeError: | ||
+ | new_chunk.buildModel(surface = PhotoScan.SurfaceType.HeightField, source = PhotoScan.DataSource.PointCloudData, interpolation = PhotoScan.Interpolation.EnabledInterpolation, face_count = PhotoScan.FaceCount.HighFaceCount) | ||
+ | if autosave: | ||
+ | doc.save() | ||
+ | if not buildDense: | ||
+ | new_chunk.dense_cloud = None | ||
+ | |||
new_chunk.depth_maps = None | new_chunk.depth_maps = None | ||
+ | #new_chunk = None | ||
if mergeBack: | if mergeBack: | ||
Line 188: | Line 259: | ||
chunk = doc.chunks[i] | chunk = doc.chunks[i] | ||
chunk.remove(chunk.cameras) | chunk.remove(chunk.cameras) | ||
− | doc.chunks[0].model = None #removing model from original chunk, just for case | + | doc.chunks[0].model = None #removing model from 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.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. | doc.remove(doc.chunks[1:-1]) #removing smaller chunks. | ||
+ | if autosave: | ||
+ | doc.save() | ||
+ | |||
+ | if autosave: | ||
+ | doc.save() | ||
print("Script finished") | print("Script finished") | ||
Line 201: | Line 278: | ||
doc = PhotoScan.app.document | doc = PhotoScan.app.document | ||
− | app = | + | app = QtWidgets.QApplication.instance() |
parent = app.activeWindow() | parent = app.activeWindow() | ||
Line 207: | Line 284: | ||
− | PhotoScan.app.addMenuItem("Custom/Split in chunks", main) | + | PhotoScan.app.addMenuItem("Custom Menu/Split in chunks", main) |
</pre> | </pre> |
Latest revision as of 15:31, 17 February 2017
#compatibility PhotoScan Pro 1.3 #allows to split the original chunk into multiple chunks with smaller bounding boxes forming a user-defined grid #building dense cloud, mesh and merging the result back is optional import PhotoScan from PySide2 import QtGui, QtCore, QtWidgets QUALITY = {"1":PhotoScan.UltraQuality, "2":PhotoScan.HighQuality, "4":PhotoScan.MediumQuality, "8":PhotoScan.LowQuality, "16":PhotoScan.LowestQuality} FILTERING = {"3":PhotoScan.NoFiltering, "0":PhotoScan.MildFiltering, "1":PhotoScan.ModerateFiltering, "2":PhotoScan.AggressiveFiltering} 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(2) self.spinX.setMaximum(20) self.spinX.setFixedSize(75, 25) self.spinY = QtWidgets.QSpinBox() self.spinY.setMinimum(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.chkDense = QtWidgets.QCheckBox("Build Dense Cloud") self.chkDense.setFixedSize(120,50) self.chkDense.setWhatsThis("Builds dense cloud for each cell in grid") 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(100, 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, 0, 0) layout.addWidget(self.spinY, 0, 1) layout.addWidget(self.chkDense, 0, 2) layout.addWidget(self.chkMesh, 0, 3) layout.addWidget(self.chkMerge, 0, 4) layout.addWidget(self.btnP1, 3, 2) layout.addWidget(self.btnQuit, 3, 3) layout.addWidget(self.txtOvp, 1, 3) layout.addWidget(self.edtOvp, 1, 4) layout.addWidget(self.chkSave, 2, 4) layout.addWidget(self.grid, 1, 0, 2, 2) 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() doc = PhotoScan.app.document chunk = doc.chunk if not chunk.transform.translation: 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. for j in range(1, partsY + 1): #creating new chunks and adjusting bounding box for i in range(1, partsX + 1): new_chunk = chunk.copy(items = [PhotoScan.DataSource.DenseCloudData]) new_chunk.label = "Chunk "+ str(i)+ "\\" + str(j) new_chunk.model = None new_region = PhotoScan.Region() new_rot = r_rotate new_center = PhotoScan.Vector([(i - 0.5) * x_scale, (j - 0.5) * y_scale, 0.5 * z_scale]) new_center = offset + new_rot * new_center new_size = PhotoScan.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 PhotoScan.app.update() if autosave: doc.save() if buildDense: if new_chunk.depth_maps: reuse_depth = True quality = QUALITY[new_chunk.depth_maps.meta['depth/depth_downscale']] filtering = FILTERING[new_chunk.depth_maps.meta['depth/depth_filter_mode']] try: new_chunk.buildDenseCloud(quality = quality, filter = filtering, keep_depth = False, reuse_depth = reuse_depth) except RuntimeError: print("Can't build dense cloud for " + chunk.label) else: reuse_depth = False try: new_chunk.buildDenseCloud(quality = PhotoScan.Quality.HighQuality, filter = PhotoScan.FilterMode.AggressiveFiltering, keep_depth = False, reuse_depth = reuse_depth) 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 = PhotoScan.SurfaceType.HeightField, source = PhotoScan.DataSource.DenseCloudData, interpolation = PhotoScan.Interpolation.EnabledInterpolation, face_count = PhotoScan.FaceCount.HighFaceCount) except RuntimeError: print("Can't build mesh for " + chunk.label) else: try: new_chunk.buildModel(surface = PhotoScan.SurfaceType.HeightField, source = PhotoScan.DataSource.PointCloudData, interpolation = PhotoScan.Interpolation.EnabledInterpolation, face_count = PhotoScan.FaceCount.HighFaceCount) except RuntimeError: new_chunk.buildModel(surface = PhotoScan.SurfaceType.HeightField, source = PhotoScan.DataSource.PointCloudData, interpolation = PhotoScan.Interpolation.EnabledInterpolation, face_count = PhotoScan.FaceCount.HighFaceCount) if autosave: doc.save() if not buildDense: new_chunk.dense_cloud = None new_chunk.depth_maps = None #new_chunk = None if mergeBack: for i in range(1, len(doc.chunks)): chunk = doc.chunks[i] chunk.remove(chunk.cameras) doc.chunks[0].model = None #removing model from 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 main(): global doc doc = PhotoScan.app.document app = QtWidgets.QApplication.instance() parent = app.activeWindow() dlg = SplitDlg(parent) PhotoScan.app.addMenuItem("Custom Menu/Split in chunks", main)