Difference between revisions of "Split in chunks.py"

From Agisoft
Jump to: navigation, search
 
Line 1: Line 1:
 
<pre>
 
<pre>
 
#compatibility PhotoScan Pro 1.3
 
#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
 
import PhotoScan

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)