Difference between revisions of "Split in chunks.py"

From Agisoft
Jump to: navigation, search
Line 1: Line 1:
 
<pre>
 
<pre>
#adds custom menu item
+
#compatibility PhotoScan Pro 1.3
#allows to split the original chunk into multiple chunks with smaller bounding boxes forming a grid
+
#building dense cloud, mesh and merging the result back is optional
+
  
 
import PhotoScan
 
import PhotoScan
from PySide import QtGui, QtCore
+
from PySide2 import QtGui, QtCore, QtWidgets
  
class SplitDlg(QtGui.QDialog):
+
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):
 
def __init__(self, parent):
  
QtGui.QDialog.__init__(self, parent)
+
QtWidgets.QDialog.__init__(self, parent)
 
self.setWindowTitle("Split in chunks")
 
self.setWindowTitle("Split in chunks")
 
 
Line 19: Line 28:
 
self.gridHeight = 198
 
self.gridHeight = 198
 
 
self.spinX = QtGui.QSpinBox()
+
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 = QtGui.QSpinBox()
+
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 = QtGui.QCheckBox("Build Mesh")
+
self.chkMesh = QtWidgets.QCheckBox("Build Mesh")
self.chkMesh.setFixedSize(130,50)
+
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 = QtGui.QCheckBox("Build Dense Cloud")
+
self.chkDense = QtWidgets.QCheckBox("Build Dense Cloud")
self.chkDense.setFixedSize(130,50)
+
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 = QtGui.QCheckBox("Merge Back")
+
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 = QtGui.QPushButton("Quit")
+
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 = QtGui.QPushButton("Split")
+
self.btnP1 = QtWidgets.QPushButton("Split")
 
self.btnP1.setFixedSize(90,50)
 
self.btnP1.setFixedSize(90,50)
 
 
self.grid = QtGui.QLabel(" ")
+
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 87:
 
self.grid.show()
 
self.grid.show()
 
 
layout = QtGui.QGridLayout()  #creating 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 95:
 
layout.addWidget(self.chkMerge, 0, 4)
 
layout.addWidget(self.chkMerge, 0, 4)
 
 
layout.addWidget(self.btnP1, 2, 2)
+
layout.addWidget(self.btnP1, 3, 2)
layout.addWidget(self.btnQuit, 2, 3)
+
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 165:
 
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 154: Line 185:
  
 
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 j in range(partsY, 0, -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.label = "Chunk "+ str(i)+ "\\" + str(partsY - j + 1)
 
 
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 167: Line 195:
 
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 174: Line 207:
 
 
 
PhotoScan.app.update()
 
PhotoScan.app.update()
 +
 +
if autosave:
 +
doc.save()
 
 
 
if buildDense:
 
if buildDense:
new_chunk.buildDenseCloud(quality = PhotoScan.Quality.MediumQuality, filter = PhotoScan.FilterMode.AggressiveFiltering)
+
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.DataSource.DenseCloudData, interpolation = PhotoScan.Interpolation.EnabledInterpolation, face_count = PhotoScan.FaceCount.HighFaceCount)
+
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.DataSource.PointCloudData, interpolation = PhotoScan.Interpolation.EnabledInterpolation, face_count = PhotoScan.FaceCount.HighFaceCount)
+
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 190: Line 256:
 
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 203: Line 275:
 
doc = PhotoScan.app.document
 
doc = PhotoScan.app.document
  
app = QtGui.QApplication.instance()
+
app = QtWidgets.QApplication.instance()
 
parent = app.activeWindow()
 
parent = app.activeWindow()
 
 
Line 209: Line 281:
 
 
 
 
PhotoScan.app.addMenuItem("Custom/Split in chunks", main)
+
PhotoScan.app.addMenuItem("Custom Menu/Split in chunks", main)
 
</pre>
 
</pre>

Revision as of 15:30, 17 February 2017

#compatibility PhotoScan Pro 1.3

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)