http://wiki.agisoft.com/w/index.php?title=Export_Individual_Orthophotos.py&feed=atom&action=history
Export Individual Orthophotos.py - Revision history
2024-03-28T13:54:49Z
Revision history for this page on the wiki
MediaWiki 1.24.1
http://wiki.agisoft.com/w/index.php?title=Export_Individual_Orthophotos.py&diff=120&oldid=prev
Alexey at 07:50, 11 August 2015
2015-08-11T07:50:28Z
<p></p>
<table class='diff diff-contentalign-left'>
<col class='diff-marker' />
<col class='diff-content' />
<col class='diff-marker' />
<col class='diff-content' />
<tr style='vertical-align: top;'>
<td colspan='2' style="background-color: white; color:black; text-align: center;">← Older revision</td>
<td colspan='2' style="background-color: white; color:black; text-align: center;">Revision as of 07:50, 11 August 2015</td>
</tr><tr><td colspan="2" class="diff-lineno">Line 267:</td>
<td colspan="2" class="diff-lineno">Line 267:</td></tr>
<tr><td class='diff-marker'> </td><td style="background-color: #f9f9f9; color: #333333; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #e6e6e6; vertical-align: top; white-space: pre-wrap;"><div> #recalculating WGS84 resolution from degrees into meters if required</div></td><td class='diff-marker'> </td><td style="background-color: #f9f9f9; color: #333333; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #e6e6e6; vertical-align: top; white-space: pre-wrap;"><div> #recalculating WGS84 resolution from degrees into meters if required</div></td></tr>
<tr><td class='diff-marker'> </td><td style="background-color: #f9f9f9; color: #333333; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #e6e6e6; vertical-align: top; white-space: pre-wrap;"><div> if chunk.crs:</div></td><td class='diff-marker'> </td><td style="background-color: #f9f9f9; color: #333333; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #e6e6e6; vertical-align: top; white-space: pre-wrap;"><div> if chunk.crs:</div></td></tr>
<tr><td class='diff-marker'>−</td><td style="color:black; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;"><div> if ('<del class="diffchange diffchange-inline">UNIT["degree"</del>' in proj.wkt):</div></td><td class='diff-marker'>+</td><td style="color:black; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;"><div> if <ins class="diffchange diffchange-inline">not </ins>('<ins class="diffchange diffchange-inline">PROJCS</ins>' in proj.wkt):</div></td></tr>
<tr><td class='diff-marker'> </td><td style="background-color: #f9f9f9; color: #333333; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #e6e6e6; vertical-align: top; white-space: pre-wrap;"><div> crd = photo.reference.location</div></td><td class='diff-marker'> </td><td style="background-color: #f9f9f9; color: #333333; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #e6e6e6; vertical-align: top; white-space: pre-wrap;"><div> crd = photo.reference.location</div></td></tr>
<tr><td class='diff-marker'> </td><td style="background-color: #f9f9f9; color: #333333; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #e6e6e6; vertical-align: top; white-space: pre-wrap;"></td><td class='diff-marker'> </td><td style="background-color: #f9f9f9; color: #333333; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #e6e6e6; vertical-align: top; white-space: pre-wrap;"></td></tr>
</table>
Alexey
http://wiki.agisoft.com/w/index.php?title=Export_Individual_Orthophotos.py&diff=97&oldid=prev
Alexey: Created page with "<pre> #Batch export of orthophotos based on individual cameras or user selected cameras #creates custom menu item #compatibility Agisoft PhotoScan Pro 1.1.0 #no arguments re..."
2015-02-06T10:08:23Z
<p>Created page with "<pre> #Batch export of orthophotos based on individual cameras or user selected cameras #creates custom menu item #compatibility Agisoft PhotoScan Pro 1.1.0 #no arguments re..."</p>
<p><b>New page</b></p><div><pre><br />
#Batch export of orthophotos based on individual cameras or user selected cameras<br />
#creates custom menu item<br />
<br />
#compatibility Agisoft PhotoScan Pro 1.1.0 <br />
#no arguments required<br />
<br />
import os<br />
import time<br />
import random<br />
import PhotoScan<br />
from PySide import QtCore, QtGui<br />
<br />
<br />
def intersect(p0, pn, l0, l):<br />
d = ((p0 - l0) * pn) / (l * pn)<br />
return d * l + l0<br />
<br />
<br />
class ExportOrthoDlg(QtGui.QDialog):<br />
<br />
def __init__(self, parent):<br />
<br />
QtGui.QDialog.__init__(self, parent)<br />
<br />
self.blend_types = {"Average": PhotoScan.BlendingMode.AverageBlending, "Mosaic": PhotoScan.BlendingMode.MosaicBlending, "Min intensity": PhotoScan.BlendingMode.MinBlending, "Max Intensity": PhotoScan.BlendingMode.MaxBlending}<br />
<br />
self.setWindowTitle("Export individual orthophotos")<br />
<br />
self.btnQuit = QtGui.QPushButton("Quit")<br />
self.btnQuit.setFixedSize(130,50)<br />
<br />
self.btnP1 = QtGui.QPushButton("Export")<br />
self.btnP1.setFixedSize(130,50)<br />
<br />
self.pBar = QtGui.QProgressBar()<br />
self.pBar.setTextVisible(False)<br />
self.pBar.setFixedSize(150, 50)<br />
<br />
<br />
self.resTxt = QtGui.QLabel()<br />
self.resTxt.setText("Export resolution (m/pix):")<br />
self.resTxt.setFixedSize(130, 25) <br />
<br />
self.blendTxt = QtGui.QLabel()<br />
self.blendTxt.setText("Blending mode:")<br />
self.blendTxt.setFixedSize(130, 25) <br />
<br />
self.blendCmb = QtGui.QComboBox() #texture type values<br />
self.blendCmb.setFixedSize(100, 25)<br />
for type in self.blend_types.keys():<br />
self.blendCmb.addItem(type)<br />
<br />
self.resEdt = QtGui.QLineEdit()<br />
self.resEdt.setPlaceholderText("export resolution (m/pix), e.g 0.01")<br />
self.resEdt.setFixedSize(100, 25)<br />
<br />
self.selTxt = QtGui.QLabel()<br />
self.selTxt.setText("Export for:")<br />
self.selTxt.setFixedSize(100, 25) <br />
<br />
self.radioBtn_all = QtGui.QRadioButton("all cameras")<br />
self.radioBtn_sel = QtGui.QRadioButton("selected cameras")<br />
self.radioBtn_rnd = QtGui.QRadioButton("random 10 cameras")<br />
<br />
self.radioBtn_all.setChecked(True)<br />
self.radioBtn_rnd.setChecked(False)<br />
self.radioBtn_sel.setChecked(False)<br />
<br />
layout = QtGui.QGridLayout() #creating layout<br />
layout.addWidget(self.resTxt, 0, 1)<br />
layout.addWidget(self.resEdt, 0, 2)<br />
layout.addWidget(self.blendTxt, 1, 1)<br />
layout.addWidget(self.blendCmb, 1, 2)<br />
layout.addWidget(self.selTxt, 0, 0)<br />
layout.addWidget(self.radioBtn_all, 1, 0)<br />
layout.addWidget(self.radioBtn_sel, 2, 0)<br />
layout.addWidget(self.radioBtn_rnd, 3, 0)<br />
layout.addWidget(self.btnP1, 4, 1)<br />
layout.addWidget(self.btnQuit, 4, 2)<br />
layout.addWidget(self.pBar, 3, 0, 5, 1)<br />
self.setLayout(layout) <br />
<br />
proc_exp = lambda : self.exp_ortho()<br />
<br />
QtCore.QObject.connect(self.btnP1, QtCore.SIGNAL("clicked()"), proc_exp)<br />
QtCore.QObject.connect(self.btnQuit, QtCore.SIGNAL("clicked()"), self, QtCore.SLOT("reject()")) <br />
<br />
self.exec()<br />
<br />
def surf_height(self, chunk, photo):<br />
<br />
points_h = list()<br />
point_cloud = chunk.point_cloud<br />
points = point_cloud.points<br />
npoints = len(points)<br />
num_valid = 0<br />
<br />
point_index = 0<br />
for proj in point_cloud.projections[photo]: <br />
<br />
track_id = proj.track_id<br />
while point_index < npoints and points[point_index].track_id < track_id:<br />
point_index += 1<br />
if point_index < npoints and points[point_index].track_id == track_id:<br />
if not points[point_index].valid: <br />
continue<br />
<br />
v = points[point_index].coord<br />
vt = chunk.transform.matrix.mulp(v)<br />
if chunk.crs:<br />
vt = chunk.crs.project(vt)<br />
points_h.append(vt[2])<br />
num_valid += 1<br />
<br />
points_h.sort()<br />
height = points_h[num_valid // 2]<br />
<br />
return height<br />
<br />
def exp_ortho(self):<br />
<br />
doc = PhotoScan.app.document<br />
chunk = doc.chunk<br />
path = doc.path.rsplit("\\", 1)[0]<br />
<br />
if not chunk.model:<br />
PhotoScan.app.messageBox("No mesh generated!\n")<br />
return False<br />
<br />
try:<br />
resolution = float(self.resEdt.text())<br />
except(ValueError):<br />
PhotoScan.app.messageBox("Incorrect export resolution! Please use point delimiter.\n")<br />
print("Script aborted.")<br />
return False<br />
<br />
print("Export started...") #information message<br />
<br />
self.btnP1.setDisabled(True)<br />
self.btnQuit.setDisabled(True)<br />
self.pBar.setMinimum(0)<br />
self.pBar.setMaximum(100)<br />
<br />
export_list = list()<br />
if self.radioBtn_sel.isChecked():<br />
for photo in chunk.cameras:<br />
if photo.selected:<br />
export_list.append(photo)<br />
elif self.radioBtn_all.isChecked():<br />
export_list = list(chunk.cameras)<br />
elif self.radioBtn_rnd.isChecked():<br />
random_cams = random.sample(range(len(chunk.cameras)), 10) #number of random cameras<br />
for i in range (0, p_num):<br />
export_list.append(chunk.cameras[random_cams[i]])<br />
for photo in chunk.cameras:<br />
photo.enabled = False<br />
<br />
blending_mode = self.blend_types[self.blendCmb.currentText()]<br />
<br />
processed = 0<br />
t0 = time.time()<br />
<br />
for i in range (0, len(chunk.cameras)):<br />
photo = chunk.cameras[i]<br />
photo.enabled = False<br />
<br />
PhotoScan.app.update()<br />
<br />
for photo in export_list: <br />
<br />
if not photo.transform:<br />
continue<br />
<br />
x0 = x1 = x2 = x3 = PhotoScan.Vector((0.0,0.0,0.0))<br />
<br />
width = photo.sensor.width<br />
height = photo.sensor.height<br />
calibration = photo.sensor.calibration<br />
<br />
# vectors corresponding to photo corners<br />
<br />
v0 = PhotoScan.Vector(( -calibration.cx / calibration.fx, -calibration.cy / calibration.fy, 1))<br />
v1 = PhotoScan.Vector(( (width - calibration.cx) / calibration.fx, -calibration.cy / calibration.fy, 1))<br />
v2 = PhotoScan.Vector(( -calibration.cx / calibration.fx, (height - calibration.cy) / calibration.fy, 1))<br />
v3 = PhotoScan.Vector(( (width - calibration.cx) / calibration.fx, (height - calibration.cy) / calibration.fy, 1))<br />
vc = photo.center <br />
<br />
v0.size = v1.size = v2.size = v3.size = vc.size = 4 <br />
v0[3] = v1[3] = v2[3] = v3[3] = 0<br />
vc[3] = 1<br />
<br />
M = chunk.transform.matrix * photo.transform<br />
<br />
v0_gc = M * v0<br />
v1_gc = M * v1<br />
v2_gc = M * v2<br />
v3_gc = M * v3<br />
vc_gc = chunk.transform.matrix * vc<br />
<br />
v0_gc.size = v1_gc.size = v2_gc.size = v3_gc.size = vc_gc.size = 3<br />
<br />
# surface normal<br />
<br />
cen_p = photo.center<br />
cen_t = chunk.transform.matrix.mulp(cen_p)<br />
if chunk.crs:<br />
cen_t = chunk.crs.project(cen_t)<br />
<br />
h = self.surf_height(chunk, photo)<br />
<br />
vloc = PhotoScan.Vector((cen_t[0], cen_t[1], h))<br />
vloc_h = PhotoScan.Vector((cen_t[0], cen_t[1], h))<br />
vloc_h[2] += 1<br />
<br />
if chunk.crs:<br />
vloc_gc = chunk.crs.unproject(vloc)<br />
vloc_h_gc = chunk.crs.unproject(vloc_h)<br />
surf_n = vloc_h_gc - vloc_gc<br />
else:<br />
vloc_gc = vloc<br />
vloc_h_gc = vloc_h<br />
surf_n = vloc_h - vloc<br />
<br />
surf_n.normalize()<br />
v0_gc.normalize()<br />
v1_gc.normalize()<br />
v2_gc.normalize()<br />
v3_gc.normalize()<br />
<br />
#intersection with the surface<br />
<br />
x0 = intersect(vloc_gc, surf_n, vc_gc, v0_gc)<br />
x1 = intersect(vloc_gc, surf_n, vc_gc, v1_gc)<br />
x2 = intersect(vloc_gc, surf_n, vc_gc, v2_gc)<br />
x3 = intersect(vloc_gc, surf_n, vc_gc, v3_gc)<br />
<br />
if chunk.crs:<br />
x0 = chunk.crs.project(x0)<br />
x1 = chunk.crs.project(x1)<br />
x2 = chunk.crs.project(x2)<br />
x3 = chunk.crs.project(x3)<br />
<br />
x_0 = min(x0[0], x1[0], x2[0], x3[0])<br />
x_1 = max(x0[0], x1[0], x2[0], x3[0])<br />
y_0 = min(x0[1], x1[1], x2[1], x3[1])<br />
y_1 = max(x0[1], x1[1], x2[1], x3[1])<br />
<br />
x_0 -= (x_1 - x_0) / 20.<br />
x_1 += (x_1 - x_0) / 20.<br />
y_0 -= (y_1 - y_0) / 20.<br />
y_1 += (y_1 - y_0) / 20.<br />
<br />
reg = (x_0, y_0, x_1, y_1)<br />
<br />
photo.enabled = True<br />
PhotoScan.app.update()<br />
p_name = photo.photo.path.rsplit("/", 1)[1].rsplit(".",1)[0]<br />
p_name = "ortho_" + p_name<br />
<br />
if chunk.crs:<br />
proj = chunk.crs ##export in chunk coordinate system<br />
else:<br />
proj = PhotoScan.Matrix().diag([1,1,1,1]) #TopXY<br />
d_x = d_y = resolution<br />
<br />
#recalculating WGS84 resolution from degrees into meters if required<br />
if chunk.crs:<br />
if ('UNIT["degree"' in proj.wkt):<br />
crd = photo.reference.location<br />
<br />
#longitude<br />
v1 = PhotoScan.Vector((crd[0], crd[1], 0) )<br />
v2 = PhotoScan.Vector((crd[0] + 0.001, crd[1], 0))<br />
vm1 = chunk.crs.unproject(v1)<br />
vm2 = chunk.crs.unproject(v2)<br />
res_x = (vm2 - vm1).norm() * 1000<br />
<br />
#latitude<br />
v2 = PhotoScan.Vector( (crd[0], crd[1] + 0.001, 0))<br />
vm2 = chunk.crs.unproject(v2)<br />
res_y = (vm2 - vm1).norm() * 1000<br />
<br />
pixel_x = pixel_y = resolution #export resolution (meters/pix)<br />
d_x = pixel_x / res_x <br />
d_y = pixel_y / res_y<br />
<br />
<br />
if chunk.exportOrthophoto(path + "\\" + p_name + ".tif", format = "tif", blending = blending_mode, color_correction = False, projection = proj, region = reg, dx = d_x, dy = d_y, write_world = True):<br />
processed +=1<br />
photo.enabled = False<br />
self.pBar.setValue(int(processed / len(export_list) * 100))<br />
<br />
for i in range (0, len(chunk.cameras)):<br />
photo = chunk.cameras[i]<br />
photo.enabled = True<br />
<br />
PhotoScan.app.update()<br />
<br />
<br />
self.btnP1.setDisabled(False)<br />
self.btnQuit.setDisabled(False)<br />
<br />
t1 = time.time()<br />
<br />
t1 -= t0<br />
t1 = int(t1)<br />
<br />
PhotoScan.app.messageBox("Processing finished.\nProcessed "+ str(processed) +" images to orthophotos.\nProcessing time: "+ str(t1) +" seconds.\nPress OK.") #information message<br />
<br />
return 1<br />
<br />
def main():<br />
<br />
global doc<br />
doc = PhotoScan.app.document<br />
<br />
app = QtGui.QApplication.instance()<br />
parent = app.activeWindow()<br />
<br />
dlg = ExportOrthoDlg(parent)<br />
<br />
<br />
PhotoScan.app.addMenuItem("Custom/Export individual orthophotos", main)<br />
</pre></div>
Alexey