Forum

Show Posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.


Messages - _mARCel_

Pages: [1] 2
1
Hi matt07lx,

we are currently using Metashape 1.5.2 and it works just fine. I suppose you would like to have the current code from us? I ask my colleague if I may insert it.  ;)

Best regards,
Marcel

2
Python and Java API / Re: Script for exporting orthophotos using markers
« on: August 28, 2018, 05:13:17 PM »
Hi Dukytony,

here is our script for 1.4.1.

Best regards,
Marcel



Code: [Select]
"""
/********************************************************************************************************************
                                    OPEX - Orthophotoexport (V. 1.10)

                                                    Content:
  Processing code to exports orthophoto in planar projection defined by vektor (from three markers) and Z axes.
  This code is mainly for exporting archaeological profiles; ready for importing in GIS.
  Before using the script you need:
  - A georeferenzed Model
  - An "A" and "B" Profile Nail (Named "A" and "B" in Photoscan)
  - A "Y" Point in Photoscan with the coordinates (X-coord from "A",Y-coord from "A", Z-coord from "A"+ a high number)

  Compatibility - PhotoScan Professional 1.4.1
  Version - 1.10

-------------------
written by         :   Alexey Pasumansky, AgiSoft LLC,
                                              Marcel C. Hagner, ArchaeoBW GmbH
      Florian Tubbesing, ArchaeoBW GmbH
copyright         : 2018 ArchaeoBW GmbH
email                : info@archaeobw.de
 ********************************************************************************************************************/

/*************************************************************************************
 *   This program is free software; you can redistribute it and/or modify it under   *
 *   under the terms of a Creative Commons Attribution-NonCommercial-ShareAlike 4.0  *
 *   International License.                                                          *
 *                                                                                   *
 *   Attribution - You must give a appropriate credit, provide a link to the license,*
 * and indicate of changes were made. You may do so in any reasonable manner, but  *
 * not in any way that suggests the licensor endorses you or your use              *
 *   *
 * NonCommercial - You may not use the material for commercial purposes.           *
 * *
 * ShareAlike - If you remix, transform, or build upon the material, you must      *
 * distribute your contributions under the same license as the original.           *
 *************************************************************************************/
"""


import os
import PhotoScan
from PySide2 import QtCore, QtWidgets

def getMarker(chunk, label):

for marker in chunk.markers:
if marker.label.upper() == label.upper():
return marker
return 0

def vect(a, b):

result = PhotoScan.Vector([a.y*b.z - a.z*b.y, a.z*b.x - a.x*b.z, a.x*b.y - a.y *b.x])
return result.normalized()

class ExportOFDlg(QtWidgets.QDialog):

def __init__ (self, parent):
QtWidgets.QDialog.__init__(self, parent)

chunk = doc.chunk
self.supported_types = ["jpg", "tif", "png"]
self.blending = PhotoScan.BlendingMode.AverageBlending


self.setWindowTitle("OPEX")

self.btnQuit = QtWidgets.QPushButton("&Exit")
self.btnQuit.setFixedSize(150,50)

self.btnP1 = QtWidgets.QPushButton("&Export")
self.btnP1.setFixedSize(150,50)

self.resTxt = QtWidgets.QLabel()
self.resTxt.setText("Export resolution (m):")
self.resTxt.setFixedSize(150, 25)

self.vectTxt = QtWidgets.QLabel()
self.vectTxt.setText("Horizontal vector:")
self.vectTxt.setFixedSize(150, 25)

self.vect2Txt = QtWidgets.QLabel()
self.vect2Txt.setText("Vertical vector:")
self.vect2Txt.setFixedSize(150, 25)

self.resEdt = QtWidgets.QLineEdit()
self.resEdt.setText("0.00025")
self.resEdt.setFixedSize(150, 25)

self.radioBtn_v = QtWidgets.QRadioButton("Vertical")
self.radioBtn_h = QtWidgets.QRadioButton("Horizontal")
self.radioBtn_v.setChecked(True)
self.radioBtn_h.setChecked(False)

self.llistV = [None, None]

self.llistV[0] = QtWidgets.QComboBox()
self.llistV[0].resize(150, 30)
self.llistV[1] = QtWidgets.QComboBox()
self.llistV[1].resize(150, 30)

self.llistH = [None, None]

self.llistH[0] = QtWidgets.QComboBox()
self.llistH[0].resize(150, 30)
self.llistH[1] = QtWidgets.QComboBox()
self.llistH[1].resize(150, 30)


self.radio = QtWidgets.QVBoxLayout()
self.radio.addWidget(self.radioBtn_v)
self.radio.addWidget(self.radioBtn_h)

for marker in chunk.markers:
for ilist in self.llistV:
ilist.addItem(marker.label)
for ilist in self.llistH:
ilist.addItem(marker.label)


layout = QtWidgets.QGridLayout()   #creating layout
layout.setSpacing(10)
layout.addWidget(self.resTxt, 0, 0)
layout.addWidget(self.resEdt, 0, 1)

layout.addWidget(self.vectTxt, 1, 0)
layout.addWidget(self.vect2Txt, 2, 0)
layout.addWidget(self.llistH[0], 1, 1)
layout.addWidget(self.llistH[1], 1, 2)
layout.addWidget(self.llistV[0], 2, 1)
layout.addWidget(self.llistV[1], 2, 2)
layout.addLayout(self.radio, 3, 0)

layout.addWidget(self.btnP1, 3, 1)
layout.addWidget(self.btnQuit, 3, 2)
self.setLayout(layout) 

proc_export = lambda : self.procExport()

QtCore.QObject.connect(self.btnP1, QtCore.SIGNAL("clicked()"), proc_export)
QtCore.QObject.connect(self.btnQuit, QtCore.SIGNAL("clicked()"), self, QtCore.SLOT("reject()"))

self.exec()

def procExport(self):

chunk = doc.chunk

path = PhotoScan.app.getExistingDirectory("Please choose the folder with your psx files.")
crs = PhotoScan.app.getCoordinateSystem("Please choose the Output Coordinate System.", )
filelist = os.listdir(path)
doclist = list()
for file in filelist:
if file[-3:].upper() == "PSX":
doclist.append(path + "\\" + file)


try:
d_x = d_y = float(self.resEdt.text())
except(ValueError):
PhotoScan.app.messageBox("Incorrect export resolution! Please use point delimiter.\n")
print("Script aborted.")
return 0

if self.llistV[0].currentIndex() == self.llistV[1].currentIndex():
PhotoScan.app.messageBox("Can't use the same marker for vector start and end.\n")
print("Script aborted.")
return 0

if self.llistH[0].currentIndex() == self.llistH[1].currentIndex():
PhotoScan.app.messageBox("Can't use the same marker for vector start and end.\n")
print("Script aborted.")
return 0

if len(chunk.markers) < 2:
PhotoScan.app.messageBox("No markers.\n")
print("Script aborted.")
return 0

print("script is starting.....")

type = "tif"

for doc_path in doclist:
try:
doc_unlock = PhotoScan.Document()
doc_unlock.open(doc_path, read_only=True)
doc_unlock.read_only = False
doc.open(doc_path, read_only=False)
chunk = doc.chunk
chunk.crs=crs
T = chunk.transform.matrix

path = doc_path[:-3] + type


self.btnP1.setDisabled(True)
self.btnQuit.setDisabled(True)

m1 = getMarker(chunk, self.llistH[0].currentText())
m2 = getMarker(chunk, self.llistH[1].currentText())

horizontal = m2.position - m1.position
horizontal = T.mulv(horizontal)

m1 = getMarker(chunk, self.llistV[0].currentText())
m2 = getMarker(chunk, self.llistV[1].currentText())

vertical = m2.position - m1.position
vertical = T.mulv(vertical)

normal = vect(vertical, horizontal)

if self.radioBtn_h.isChecked():
vertical = vect(horizontal, normal)
horizontal = horizontal.normalized()

elif self.radioBtn_v.isChecked():
vertical = vertical.normalized()
horizontal = -vect(vertical, normal)
#horizontal, vertical = -vertical, -horizontal

else:
PhotoScan.app.messageBox("Error! Can't export orthomosaic." + path)
print("Script aborted")
continue

m = -1
for marker in chunk.markers:#Choosing marker by name
m += 1
label = marker.label
if "A" in label:
a = m
R = PhotoScan.Matrix ([horizontal, vertical, -normal])
origin = T.mulp(chunk.markers[a].position)
A = m1.reference.location * PhotoScan.Vector([0,0,1]) #A is m1´s height
X = (-1) * R * origin

horizontal.size = 4
horizontal.w = X.x
vertical.size = 4
vertical.w = X.y + A # A is added
normal.size = 4
normal.w = -X.z

proj = PhotoScan.Matrix ([horizontal, vertical, -normal, PhotoScan.Vector([0,0,0,1])])

if doc.path[-3:].lower() != "psx":  #saving in PSX required to build orthomosaic
doc.save(doc.path[:-3] + "psx")
chunk = doc.chunk

chunk.buildOrthomosaic(surface = PhotoScan.DataSource.ModelData, blending = self.blending, projection = proj, dx = d_x, dy = d_y)
doc.save()
chunk.exportOrthomosaic(path, dx = d_x, dy = d_y)

self.btnP1.setDisabled(False)
self.btnQuit.setDisabled(False)
PhotoScan.app.update()

except:
continue
print("Finished process")
print()
return 1



def main():

global doc
doc = PhotoScan.app.document

app = QtWidgets.QApplication.instance()
parent = app.activeWindow()

dlg = ExportOFDlg(parent)

PhotoScan.app.addMenuItem("ArchaeoBW Toolbox/OPEX", main)

3
Python and Java API / Re: Script for exporting orthophotos using markers
« on: November 23, 2016, 02:34:35 PM »
Hi,
i think the problem is, that "my" script needs 6 Markers...so try this one...
Code: [Select]
# Compatibility - PhotoScan Professional 1.2.6
# exports orthophoto in planar projection defined by vector (from three markers) and Z axes.
# script version 0.9
# written by Alexey Pasumansky, AgiSoft LLC
# expanded by Marcel C. Hagner for ArchaeoBW 2016
import os
import PhotoScan
from PySide import QtCore, QtGui

def getMarker(chunk, label):

for marker in chunk.markers:
if marker.label.upper() == label.upper():
return marker
return 0

def vect(a, b):

result = PhotoScan.Vector([a.y*b.z - a.z*b.y, a.z*b.x - a.x*b.z, a.x*b.y - a.y *b.x])
return result.normalized()

class ExportDlg(QtGui.QDialog):

def __init__ (self, parent):
QtGui.QDialog.__init__(self, parent)

chunk = doc.chunk
self.supported_types = ["jpg", "tif", "png"]
self.blending = PhotoScan.BlendingMode.MosaicBlending


self.setWindowTitle("OPEX")

self.btnQuit = QtGui.QPushButton("&Exit")
self.btnQuit.setFixedSize(150,50)

self.btnP1 = QtGui.QPushButton("&Export")
self.btnP1.setFixedSize(150,50)

self.resTxt = QtGui.QLabel()
self.resTxt.setText("Export resolution (m):")
self.resTxt.setFixedSize(150, 25)

self.vectTxt = QtGui.QLabel()
self.vectTxt.setText("Horizontal vector:")
self.vectTxt.setFixedSize(150, 25)

self.Btn_cc = QtGui.QCheckBox("Color Correction")
self.Btn_cc.setChecked(True)

self.vect2Txt = QtGui.QLabel()
self.vect2Txt.setText("Vertical vector:")
self.vect2Txt.setFixedSize(150, 25)

self.resEdt = QtGui.QLineEdit()
self.resEdt.setText("0.00025")
self.resEdt.setFixedSize(150, 25)

self.radioBtn_v = QtGui.QRadioButton("Vertical")
self.radioBtn_h = QtGui.QRadioButton("Horizontal")
self.radioBtn_v.setChecked(True)
self.radioBtn_h.setChecked(False)

self.llistV = [None, None]

self.llistV[0] = QtGui.QComboBox()
self.llistV[0].resize(150, 30)
self.llistV[1] = QtGui.QComboBox()
self.llistV[1].resize(150, 30)

self.llistH = [None, None]

self.llistH[0] = QtGui.QComboBox()
self.llistH[0].resize(150, 30)
self.llistH[1] = QtGui.QComboBox()
self.llistH[1].resize(150, 30)


self.radio = QtGui.QVBoxLayout()
self.radio.addWidget(self.radioBtn_v)
self.radio.addWidget(self.radioBtn_h)

for marker in chunk.markers:
for ilist in self.llistV:
ilist.addItem(marker.label)
for ilist in self.llistH:
ilist.addItem(marker.label)


layout = QtGui.QGridLayout()   #creating layout
layout.setSpacing(10)
layout.addWidget(self.resTxt, 0, 0)
layout.addWidget(self.resEdt, 0, 1)

layout.addWidget(self.vectTxt, 1, 0)
layout.addWidget(self.vect2Txt, 2, 0)
layout.addWidget(self.llistH[0], 1, 1)
layout.addWidget(self.llistH[1], 1, 2)
layout.addWidget(self.llistV[0], 2, 1)
layout.addWidget(self.llistV[1], 2, 2)
layout.addWidget(self.Btn_cc, 0, 2)
layout.addLayout(self.radio, 3, 0)

layout.addWidget(self.btnP1, 3, 1)
layout.addWidget(self.btnQuit, 3, 2)
self.setLayout(layout) 

proc_export = lambda : self.procExport()

QtCore.QObject.connect(self.btnP1, QtCore.SIGNAL("clicked()"), proc_export)
QtCore.QObject.connect(self.btnQuit, QtCore.SIGNAL("clicked()"), self, QtCore.SLOT("reject()"))

self.exec()

def procExport(self):

chunk = doc.chunk

path = PhotoScan.app.getExistingDirectory("Bitte wählen Sie den Ordner mit den gesammelten Geo-Cut-Modell aus")
filelist = os.listdir(path)
doclist = list()
for file in filelist:
if file[-3:].upper() == "PSX":
doclist.append(path + "\\" + file)


try:
d_x = d_y = float(self.resEdt.text())
except(ValueError):
PhotoScan.app.messageBox("Incorrect export resolution! Please use point delimiter.\n")
print("Script aborted.")
return 0

if self.llistV[0].currentIndex() == self.llistV[1].currentIndex():
PhotoScan.app.messageBox("Can't use the same marker for vector start and end.\n")
print("Script aborted.")
return 0

if self.llistH[0].currentIndex() == self.llistH[1].currentIndex():
PhotoScan.app.messageBox("Can't use the same marker for vector start and end.\n")
print("Script aborted.")
return 0

if len(chunk.markers) < 2:
PhotoScan.app.messageBox("No markers.\n")
print("Script aborted.")
return 0

print("Skript startet...")

type = "tif"

for doc_path in doclist:
doc.open(doc_path)
chunk = doc.chunk
T = chunk.transform.matrix

path = doc_path[:-3] + type


self.btnP1.setDisabled(True)
self.btnQuit.setDisabled(True)

m1 = getMarker(chunk, self.llistH[0].currentText())
m2 = getMarker(chunk, self.llistH[1].currentText())

horizontal = m2.position - m1.position
horizontal = T.mulv(horizontal)

m1 = getMarker(chunk, self.llistV[0].currentText())
m2 = getMarker(chunk, self.llistV[1].currentText())

vertical = m2.position - m1.position
vertical = T.mulv(vertical)

normal = vect(vertical, horizontal)

if self.radioBtn_h.isChecked():
vertical = vect(horizontal, normal)
horizontal = horizontal.normalized()

elif self.radioBtn_v.isChecked():
vertical = vertical.normalized()
horizontal = -vect(vertical, normal)
#horizontal, vertical = -vertical, -horizontal

else:
PhotoScan.app.messageBox("Fehler!Kann das OF nicht exportieren" + path)
print("Skript wird abgebrochen.")
continue

R = PhotoScan.Matrix ([horizontal, vertical, -normal])
origin = T.mulp(chunk.markers[0].position)
X = (-1) * R * origin

horizontal.size = 4
horizontal.w = X.x
vertical.size = 4
vertical.w = X.y
normal.size = 4
normal.w = -X.z

proj = PhotoScan.Matrix ([horizontal, vertical, -normal, PhotoScan.Vector([0,0,0,1])])

if self.Btn_cc.isChecked():
color_correction = color_correction=(True)

elif not self.Btn_cc.isChecked():
color_correction = color_correction=(False)

if doc.path[-3:].lower() != "psx":  #saving in PSX required to build orthomosaic
doc.save(doc.path[:-3] + "psx")
chunk = doc.chunk

chunk.buildOrthomosaic(surface = PhotoScan.DataSource.ModelData, blending = self.blending, color_correction = color_correction, projection = proj, dx = d_x, dy = d_y)
doc.save()
chunk.exportOrthomosaic(path, dx = d_x, dy = d_y)

self.btnP1.setDisabled(False)
self.btnQuit.setDisabled(False)
PhotoScan.app.update()
print("Orthofoto wird exportiert nach to:\n" + path)

print("Skript rum.Viele Grüße MP")
print()
return 1



def main():

global doc
doc = PhotoScan.app.document

app = QtGui.QApplication.instance()
parent = app.activeWindow()

dlg = ExportDlg(parent)

PhotoScan.app.addMenuItem("OPEX(Mosaic)/OPEX for 1.2.6", main)

Your model is georeferenced?
Did you create the orthomosaic for the psx?

Best regards,
Marcel

4
Python and Java API / Re: Script for exporting orthophotos using markers
« on: November 22, 2016, 01:08:01 PM »
Hi tonio,
the script needs markers to start corretly. So, please open a psx-project with already set markers before using the script. If you don't have any markers in your project, nothing happens. Sorry for my English...

Best regards,
Marcel

5
Python and Java API / Re: Script for exporting orthophotos using markers
« on: September 29, 2016, 09:33:56 PM »
 Hello Alexey,

alright. Thanks for your fast reply!

Best regards,
Marcel

6
Python and Java API / Re: Script for exporting orthophotos using markers
« on: September 29, 2016, 06:48:12 PM »
HI everyone,

i have a small problem with my exported orthomosaics (PSpro64bit, 1.2.6). There are sometimes (random?) additional "pixels" in the edge region of the exported orthomosaic raster. They seems to be based/limited on the image size of the raster.
An example is attached. The basic data is the same!

Has that happed to you, too?

7
Python and Java API / Re: Script for exporting orthophotos using markers
« on: February 10, 2016, 06:01:52 PM »
Hello Alexey,

thank you so much for your fast reply! Yeah, now the script works again.
And thanks for the hint with the index 4 in the line where the origin is defined. But for my purpose "4"  is correct :-)


Keep it up!
Best regards,
Marcel

8
Python and Java API / Re: Script for exporting orthophotos using markers
« on: February 10, 2016, 02:23:17 PM »
Hi everyone,
can anybody help me to update my script for 1.2.3?
Thanks a lot!

Code: [Select]
# Compatibility - PhotoScan Professional 1.1.6
# exports orthophoto in planar projection defined by vector (from three markers) and Z axes.
# script version 0.9
# written by Alexey Pasumansky, AgiSoft LLC
# expanded by Marcel C. Hagner for LAD@RPS 2015
import os
import PhotoScan
from PySide import QtCore, QtGui

def getMarker(chunk, label):

for marker in chunk.markers:
if marker.label.upper() == label.upper():
return marker
return 0

def vect(a, b):

result = PhotoScan.Vector([a.y*b.z - a.z*b.y, a.z*b.x - a.x*b.z, a.x*b.y - a.y *b.x])
return result.normalized()

class ExportDlg(QtGui.QDialog):

def __init__ (self, parent):
QtGui.QDialog.__init__(self, parent)

chunk = doc.chunk
self.supported_types = ["jpg", "tif", "png"]
self.blending = PhotoScan.AverageBlending


self.setWindowTitle("OPEX")

self.btnQuit = QtGui.QPushButton("&Exit")
self.btnQuit.setFixedSize(150,50)

self.btnP1 = QtGui.QPushButton("&Export")
self.btnP1.setFixedSize(150,50)

self.resTxt = QtGui.QLabel()
self.resTxt.setText("Export resolution (m):")
self.resTxt.setFixedSize(150, 25)

self.vectTxt = QtGui.QLabel()
self.vectTxt.setText("Horizontal vector:")
self.vectTxt.setFixedSize(150, 25)

self.Btn_cc = QtGui.QCheckBox("Color Correction")
self.Btn_cc.setChecked(True)

self.vect2Txt = QtGui.QLabel()
self.vect2Txt.setText("Vertical vector:")
self.vect2Txt.setFixedSize(150, 25)

self.resEdt = QtGui.QLineEdit()
self.resEdt.setPlaceholderText("type in export resolution, e.g 0.01")
self.resEdt.setFixedSize(150, 25)

self.radioBtn_v = QtGui.QRadioButton("Vertical")
self.radioBtn_h = QtGui.QRadioButton("Horizontal")
self.radioBtn_v.setChecked(True)
self.radioBtn_h.setChecked(False)

self.llistV = [None, None]

self.llistV[0] = QtGui.QComboBox()
self.llistV[0].resize(150, 30)
self.llistV[1] = QtGui.QComboBox()
self.llistV[1].resize(150, 30)

self.llistH = [None, None]

self.llistH[0] = QtGui.QComboBox()
self.llistH[0].resize(150, 30)
self.llistH[1] = QtGui.QComboBox()
self.llistH[1].resize(150, 30)


self.radio = QtGui.QVBoxLayout()
self.radio.addWidget(self.radioBtn_v)
self.radio.addWidget(self.radioBtn_h)

for marker in chunk.markers:
for ilist in self.llistV:
ilist.addItem(marker.label)
for ilist in self.llistH:
ilist.addItem(marker.label)


layout = QtGui.QGridLayout()   #creating layout
layout.setSpacing(10)
layout.addWidget(self.resTxt, 0, 0)
layout.addWidget(self.resEdt, 0, 1)

layout.addWidget(self.vectTxt, 1, 0)
layout.addWidget(self.vect2Txt, 2, 0)
layout.addWidget(self.llistH[0], 1, 1)
layout.addWidget(self.llistH[1], 1, 2)
layout.addWidget(self.llistV[0], 2, 1)
layout.addWidget(self.llistV[1], 2, 2)
layout.addWidget(self.Btn_cc, 0, 2)
layout.addLayout(self.radio, 3, 0)

layout.addWidget(self.btnP1, 3, 1)
layout.addWidget(self.btnQuit, 3, 2)
self.setLayout(layout) 

proc_export = lambda : self.procExport()

QtCore.QObject.connect(self.btnP1, QtCore.SIGNAL("clicked()"), proc_export)
QtCore.QObject.connect(self.btnQuit, QtCore.SIGNAL("clicked()"), self, QtCore.SLOT("reject()"))

self.exec()

def procExport(self):

chunk = doc.chunk

path = PhotoScan.app.getExistingDirectory("Bitte wählen Sie den Ordner mit den gesammelten Geo-Cut-Modell aus")
filelist = os.listdir(path)
doclist = list()
for file in filelist:
if file[-3:].upper() == "PSZ":
doclist.append(path + "\\" + file)


try:
d_x = d_y = float(self.resEdt.text())
except(ValueError):
PhotoScan.app.messageBox("Incorrect export resolution! Please use point delimiter.\n")
print("Script aborted.")
return 0

if self.llistV[0].currentIndex() == self.llistV[1].currentIndex():
PhotoScan.app.messageBox("Can't use the same marker for vector start and end.\n")
print("Script aborted.")
return 0

if self.llistH[0].currentIndex() == self.llistH[1].currentIndex():
PhotoScan.app.messageBox("Can't use the same marker for vector start and end.\n")
print("Script aborted.")
return 0

if len(chunk.markers) < 2:
PhotoScan.app.messageBox("No markers.\n")
print("Script aborted.")
return 0

print("Skript startet...")

type = "tif"
for doc_path in doclist:
doc.open(doc_path)
chunk = doc.chunk
T = chunk.transform.matrix

path = doc_path[:-3] + type


self.btnP1.setDisabled(True)
self.btnQuit.setDisabled(True)

m1 = getMarker(chunk, self.llistH[0].currentText())
m2 = getMarker(chunk, self.llistH[1].currentText())

horizontal = m2.position - m1.position
horizontal = T.mulv(horizontal)

m1 = getMarker(chunk, self.llistV[0].currentText())
m2 = getMarker(chunk, self.llistV[1].currentText())

vertical = m2.position - m1.position
vertical = T.mulv(vertical)

normal = vect(vertical, horizontal)

if self.radioBtn_h.isChecked():
vertical = vect(horizontal, normal)
horizontal = horizontal.normalized()

elif self.radioBtn_v.isChecked():
horizontal = -vect(vertical, normal)
#horizontal, vertical = -vertical, -horizontal

else:
PhotoScan.app.messageBox("Fehler!Kann das OF nicht exportieren" + path)
print("Skript wird abgebrochen.")
continue

R = PhotoScan.Matrix ([horizontal, vertical, -normal])
origin = T.mulp(chunk.markers [4].position)
A = m1.reference.location * PhotoScan.Vector([0,0,1]) #A ist der Höhenwert von m1
X = (-1) * R * origin

horizontal.size = 4
horizontal.w = X.x
vertical.size = 4
vertical.w = X.y + A # hier wird A hinzuaddiert
normal.size = 4
normal.w = -X.z

proj = PhotoScan.Matrix ([horizontal, vertical, -normal, PhotoScan.Vector([0,0,0,1])])

if self.Btn_cc.isChecked():
color_correction = color_correction=(True)

elif not self.Btn_cc.isChecked():
color_correction = color_correction=(False)

chunk.exportOrthophoto(path, type, self.blending,color_correction, projection = proj, dx = d_x, dy = d_y)

self.btnP1.setDisabled(False)
self.btnQuit.setDisabled(False)
PhotoScan.app.update()
print("Orthofoto wird exportiert nach to:\n" + path)

print("Skript rum.Viele Grüße MP")
print()
return 1



def main():

global doc
doc = PhotoScan.app.document

app = QtGui.QApplication.instance()
parent = app.activeWindow()

dlg = ExportDlg(parent)

PhotoScan.app.addMenuItem("ArchaeoTools/OPEX", main)

9
Python and Java API / Re: Loops and Sleeps in Python startup script
« on: January 18, 2016, 04:58:10 PM »
Ok,

this is it:


Code: [Select]
import PhotoScan
import datetime
import glob
import os

'''
A workflow to process multiple set of photos in Photoscan.
Expects a directory structure like this:
<HomeDirectory>
Directory1
<PhotosDirectory>
photo1.tif
photo2.tif
Directory2
<PhotosDirectory>
photo1.tif
photo2.tif
It will put a PhotoScan project file in the base of each directory named <directory>.psz.
All the photos from the <PhotosPdirectory> will be added to the project.
The workflow produces an fbx and texture for each directory, which will be placed in the export directory.
This script can be run multiple times to continue processing after a crashed or cancelled processing run. It
will skip stages that have already been completed.
'''
class Profilskriptdlg(QtGui.QDialog):
def __init__(self, parent):

QtGui.QDialog.__init__(self, parent)
app = QtGui.QApplication.instance()
parent = app.activeWindow()
msg = "Hi!. Do you want to start the script?"
reply = QtGui.QMessageBox.question(parent, 'HSM-Profil-Skript V 1.0', msg, QtGui.QMessageBox.Yes, QtGui.QMessageBox.No)

if reply == QtGui.QMessageBox.Yes:
import PhotoScan, os
## Configuration
HomeDirectory = "D:\\Home" # Hier muss der Hauptpfad eingetragen werden.
PhotosDirectory = "Photos" # Hier sind jeweils die SfM-Fotos abgespeichert
ExportDirectory = "Export" # Hier werden jeweils u.a. Orthofotos abgespeichert
eingabe = PhotoScan.app.getString("Please enter the prefix")

match_photos_config = {
'accuracy': PhotoScan.LowAccuracy, # Qualität Schritt 1: Low kann mit Medium oder High getauscht werden
'preselection': PhotoScan.NoPreselection,
}


build_point_cloud_config = {}


build_dense_cloud_config = {
'quality': PhotoScan.LowQuality # Qualität Schritt 1: Low kann mit Lowest, Medium, High oder UltraHigh getauscht werden
}

build_model_config = {
'surface': PhotoScan.Arbitrary,
'interpolation': PhotoScan.EnabledInterpolation
}

export_model_config = {
'binary': True,
'texture_format': "tif",
'texture': True,
'normals': True,
'colors': True,
'cameras': False,
'format': "fbx"
}

build_uv_config = {
'mapping': PhotoScan.GenericMapping
}

build_texture_config = {
'blending': PhotoScan.BlendingMode.AverageBlending, # Hier kann die
'color_correction': False,
'size': 4096
}

#################################################


class WorkflowJob(object):
def __init__(self, name, precond_func, run_func):
self.name = name
self.precond_func = precond_func
self.run_func = run_func

def can_run(self, chunk):
return self.precond_func(chunk)

def run(self, chunk):
log( "Running job: " + self.name )
return self.run_func(chunk)



#################################################
# Logging

log_file = None
def log(msg):
global log_file
print(msg)
if log_file:
log_file.write(str(msg)+'\n')
log_file.flush()


def open_log(path):
global log_file
log_file = open(os.path.join(path,'workflow.log'), 'w')


def close_log():
global log_file
log_file.close()

def log_time():
log( datetime.datetime.now() )


#################################################
# Utility functions

def does_project_exist(project_dir):
'Does the PhotoScan project file exist'
project_name = make_project_filename(project_dir, "psz")
return glob.glob(project_dir + "\\*.psz")


def are_cameras_aligned(chunk):
'Assume cameras are aligned if at least one of them have been moved.'
return len([c for c in chunk.cameras if c.center is not None]) > 0


def export_file_exists(project_directory, format):
model_name = make_export_filename(project_directory, format)
return os.path.isfile( model_name )


def is_valid_project_dir( project_dir, photos_dir ):
"A valid project directory has a photos subdirectory."
return photos_dir.upper() in (d.upper() for d in os.listdir(project_dir))


def align_cameras(chunk):
'Aligns the cameras and returns whether it succeeded or not.'
chunk.alignCameras()
return are_cameras_aligned(chunk)


def estimate_image_quality(photo):
return Photoscan.Utils.estimateImageQuality(photo)


def get_export_path(project_dir):
dir_name = os.path.basename(project_dir)
return os.path.join(project_dir, ExportDirectory)


def make_project_filename(project_dir, ext):
'Make a filename in the project directory with the given extension'
dir_name = os.path.basename(project_dir)
return os.path.join(project_dir,eingabe + dir_name + "." + ext);


def make_export_filename(project_dir, ext):
"Make a filename in the project's export directory"
dir_name = os.path.basename(project_dir)
export_path = get_export_path(project_dir)
return os.path.join(export_path, dir_name + "." + ext);


def export_model(chunk, project_directory, kwargs):
model_name = make_export_filename(project_directory, kwargs['format'])
return chunk.exportModel(model_name, **kwargs)


def export_texture(chunk, project_directory):
texture_name = make_export_filename(project_directory, "jpg")
return chunk.model.saveTexture(texture_name)


#################################################
# Dumping

def dump_meta(meta, fp):
if meta and meta.keys:
fp.write("Meta:\n")
fp.write(str(meta) + "\n")


def dump_camera_data(cam, fp):
dump_meta(cam.meta, fp)
fp.write("Photo Path: " + cam.photo.path + "\n")
fp.write("Center: " + str(cam.center) + "\n")


def log_chunk_data(chunk):
log("Number of cameras: {}".format(len(chunk.cameras)))
log("Cameras are aligned: {}".format(are_cameras_aligned(chunk)))
log("Has sparse cloud: {}".format(chunk.point_cloud is not None))
log("Has dense cloud: {}".format(chunk.dense_cloud is not None))
log("Has model: {}".format(chunk.model is not None))


def dump_chunk_data(chunk, fp):
fp.write("Number of cameras: {}\n".format(len(chunk.cameras)))
fp.write("Cameras are aligned: {}\n".format(are_cameras_aligned(chunk)))
fp.write("Has sparse cloud: {}\n".format(chunk.point_cloud is not None))
fp.write("Has dense cloud: {}\n".format(chunk.dense_cloud is not None))
fp.write("Has model: {}\n".format(chunk.model is not None))
fp.write("Camera offset: loc: {} rot: {}\n".format(chunk.camera_offset.location, chunk.camera_offset.rotation))
fp.write("Camera accuracy: {}\n".format(chunk.accuracy_cameras))
fp.write("Markers accuracy: {}\n".format(chunk.accuracy_markers))
fp.write("Projections accuracy: {}\n".format(chunk.accuracy_projections))
fp.write("Tie points accuracy: {}\n".format(chunk.accuracy_tiepoints))
dump_meta(chunk.meta, fp)

fp.write("Camera Info:\n")
[dump_camera_data(p, fp) for p in chunk.cameras]


def dump(doc):
'Write information about the PhotoScan.Document to a file'
print( "Dumping: " + doc.path)
fp = open(os.path.join(os.path.dirname(doc.path),'workflow.txt'), 'w')
dump_meta(doc.meta, fp)
log_chunk_data(doc.chunk)
dump_chunk_data(doc.chunk, fp)
fp.close()


#################################################
# Project functions
while True:
def find_project_folders(home_dir, photos_dir):
'Find all the valid project folders in the home_dir'
subdirs = os.listdir(home_dir)
subdirs = [os.path.join(home_dir,sd) for sd in subdirs]
subdirs = [sd for sd in subdirs if os.path.isdir(sd)]
subdirs = [sd for sd in subdirs if is_valid_project_dir(sd, photos_dir)]
return subdirs


def open_project(project_dir):
log( "Opening project " + project_dir )
project_name = make_project_filename(project_dir, "psz")
doc = PhotoScan.Document()
if not doc.open( project_name ):
log( "ERROR: Cold not open document: " + project_name)
return doc


def make_project(project_dir, photos_dir):
'Make a new project and add the photos from the photos_dir to it.'

log( "Making project " + project_dir )

doc = PhotoScan.Document() # Operate on a new document for batch proecssing
#doc = PhotoScan.app.document # Use the current open document in PhotoScan

# Add the photos to a chunk
chunk = doc.addChunk()
photos_dir = os.path.join( project_dir, photos_dir )
photos = os.listdir(photos_dir)
photos = [os.path.join(photos_dir,p) for p in photos]
log( "Found {} photos in {}".format(len(photos), photos_dir))
if not chunk.addPhotos(photos):
log( "ERROR: Failed to add photos: " + str(photos))

# Save the new project
project_name = make_project_filename(project_dir, "psz")
log( "Saving: " + project_name );
if not doc.save( project_name ):
log( "ERROR: Failed to save project: " + project_name)

return doc


def make_or_open_project(project_dir, photos_dir):
if does_project_exist(project_dir):
return open_project(project_dir)
else:
return make_project(project_dir, photos_dir)



###########################################################################################################


def build(project_dir, photos_dir, jobs):
log( "Building document: " + project_dir )
log_time()

doc = make_or_open_project(project_dir, photos_dir)

export_path = get_export_path(project_dir)
if not os.path.exists(export_path):
os.makedirs(export_path)

chunk = doc.chunk

if not chunk:
log( "ERROR: Chunk is None")
return False

if not chunk.enabled:
log( "Chunk not enabled, skipping" )

# For use by jobs
chunk.label = project_dir

for job in jobs:
if job.can_run( chunk ):
if job.run( chunk ):
log( "Job finished - " + job.name )
doc.save()
else:
log( "ERROR: Job failed - " + job.name )
return False
else:
log( "Skipping job " + job.name )


log( "Finished building chunk" )
log_time()

return True


###########################################################################################################
# Workflow jobs

match_photos_job = WorkflowJob(
"Match photos",
lambda chunk: not are_cameras_aligned(chunk),
lambda chunk: chunk.matchPhotos(**match_photos_config)
)

align_cameras_job = WorkflowJob(
"Align Cameras",
lambda chunk: not are_cameras_aligned(chunk),
lambda chunk: align_cameras(chunk)
)

build_point_cloud_job = WorkflowJob(
"Build point cloud",
lambda chunk: not chunk.point_cloud,
lambda chunk: chunk.buildPoints(**build_point_cloud_config)
)

build_dense_cloud_job = WorkflowJob(
"Build dense cloud",
lambda chunk: not chunk.dense_cloud,
lambda chunk: chunk.buildDenseCloud(**build_dense_cloud_config)
)

build_model_job = WorkflowJob(
"Build model",
lambda chunk: not chunk.model,
lambda chunk: chunk.buildModel(**build_model_config)
)

build_uv_job = WorkflowJob(
"Build UV",
lambda chunk: not chunk.model.texture(),
lambda chunk: chunk.buildUV(**build_uv_config)
)

build_texture_job = WorkflowJob(
"Build texture",
lambda chunk: not chunk.model.texture(),
lambda chunk: chunk.buildTexture(**build_texture_config)
)

export_model_job = WorkflowJob(
"Export model",
lambda chunk: chunk.model and not export_file_exists(chunk.label, export_model_config['format']),
lambda chunk: export_model( chunk, chunk.label, export_model_config )
)

export_texture_job = WorkflowJob(
"Export texture",
lambda chunk: chunk.model.texture() and not export_file_exists(chunk.label, export_model_config['texture_format']),
lambda chunk: export_texture( chunk, chunk.label )
)

###########################################################################################################

open_log( HomeDirectory )

log( "--- Starting workflow ---" )
log( "Photoscan version " + PhotoScan.Application().version )
log( "Home directory: " + HomeDirectory )
log_time()

project_dirs = find_project_folders(HomeDirectory, PhotosDirectory)
log( "Found {} project directories".format( len(project_dirs) ) )

log( "Making projects" )

jobs = (
match_photos_job,
align_cameras_job,
build_point_cloud_job,
build_dense_cloud_job,
build_model_job,
build_uv_job,
build_texture_job,
export_model_job,
#export_texture_job
)

[build(pd, PhotosDirectory, jobs) for pd in project_dirs]

log_time()
log( "--- Finished workflow ---")
close_log()
time.sleep(20) # Nach Abschluss des Skripts d.h. alle Ordner sind durchsucht und *.psz erstellt worden, wartet dann Skript 20 Sekunden bevor es neu startet. (Sleep for 20sec)
def main():

global doc
doc = PhotoScan.app.document

app = QtGui.QApplication.instance()
parent = app.activeWindow()

dlg = Profilskriptdlg(parent)

PhotoScan.app.addMenuItem("ArchaeoTools/Server", main)
The "raw" script of this is from johnsietsma!!!
https://github.com/notoes/PhotoscanWorkflow/blob/master/workflow.py
Forum: http://www.agisoft.com/forum/index.php?topic=4312.0
I use it for creating orthophotos of archaeological profiles.
https://www.academia.edu/12557650/The_Use_of_Structure_from_Motion_for_the_Documentation_of_Archaeological_Profiles._The_Introduction_of_a_first_workflow_to_creating_a_profile_drawing_with_Agisoft_Photoscan_Professional_QGIS_and_GeoTiffExaminer_-_The_English_Translation
If you have some questions simply ask.

Best regards,
mARCel

10
Python and Java API / Re: Loops and Sleeps in Python startup script
« on: January 12, 2016, 01:00:40 PM »
Hi e.berkun-drevnig,

I’m doing the same with my script, but it does’t seems to be a problem for me. Why do you want to “use” the GUI at the same time with the script? The script runs as a “server” and it should be able to be started and stopped.
Sorry for my English…
Best regards,
_mARCel_

11
Python and Java API / Re: Script for exporting orthophotos using markers
« on: October 18, 2015, 09:04:24 PM »
Hello Alexey,

thank you so much!!! It works! Yeah!

12
Python and Java API / Re: Script for exporting orthophotos using markers
« on: October 14, 2015, 02:16:14 PM »
Hi Alexey,

did you have time to solve my problem with the point of origin? :-\

Thanks a lot!

13
Python and Java API / Re: Script for exporting orthophotos using markers
« on: September 29, 2015, 03:42:25 PM »
Hello Alexey,
yes, the scale is very important for me.

Thanks a lot!!!

14
Python and Java API / Re: Script for exporting orthophotos using markers
« on: September 26, 2015, 11:05:02 AM »
Hello Alexey,

thank you so much! Basically ist works perfect! There is only one thing left, that is very important for me.
The point of origin of the exported Tif looks randomly. (e.g.  -19819.792 , 6365717.397).
When i use the export function over the UI, the point of origin of the Tif is (0 , 0)
But it would be perfect, if the exported orthophoto have his point of origin at (0 , Z-Value of Marker "m1" or "A").
Is this possible?  ;D
Thank you for your efforts!

This is my "extended" script:
+ color correction checkbox
+ type = tif
+ write_world=True
+ average blending

Code: [Select]
# Compatibility - PhotoScan Professional 1.1.6
# exports orthophoto in planar projection defined by vector (from two markers) and Z axes.
# script version 1.2
# added Color_Correction Checkbox
import os
import PhotoScan
from PySide import QtCore, QtGui

def getMarker(chunk, label):

for marker in chunk.markers:
if marker.label.upper() == label.upper():
return marker
return 0

def vect(a, b):

result = PhotoScan.Vector([a.y*b.z - a.z*b.y, a.z*b.x - a.x*b.z, a.x*b.y - a.y *b.x])
return result.normalized()

class ExportDlg(QtGui.QDialog):

def __init__ (self, parent):
QtGui.QDialog.__init__(self, parent)

chunk = doc.chunk
self.supported_types = ["jpg", "tif", "png"]
self.blending = PhotoScan.AverageBlending #prefer average blending


self.setWindowTitle("Export orthophoto in planar projection")

self.btnQuit = QtGui.QPushButton("&Exit")
self.btnQuit.setFixedSize(150,50)

self.btnP1 = QtGui.QPushButton("&Export")
self.btnP1.setFixedSize(150,50)

self.resTxt = QtGui.QLabel()
self.resTxt.setText("Export resolution (m):")
self.resTxt.setFixedSize(150, 25)

self.Btn_cc = QtGui.QCheckBox("Color Correction")
self.Btn_cc.setChecked(True)

self.vectTxt = QtGui.QLabel()
self.vectTxt.setText("Horizontal vector:")
self.vectTxt.setFixedSize(150, 25)

self.vect2Txt = QtGui.QLabel()
self.vect2Txt.setText("Vertical vector:")
self.vect2Txt.setFixedSize(150, 25)

self.resEdt = QtGui.QLineEdit()
self.resEdt.setPlaceholderText("type in export resolution, e.g 0.01")
self.resEdt.setFixedSize(150, 25)

self.radioBtn_v = QtGui.QRadioButton("Vertical")
self.radioBtn_h = QtGui.QRadioButton("Horizontal")
self.radioBtn_v.setChecked(True)
self.radioBtn_h.setChecked(False)

self.llistV = [None, None]

self.llistV[0] = QtGui.QComboBox()
self.llistV[0].resize(150, 30)
self.llistV[1] = QtGui.QComboBox()
self.llistV[1].resize(150, 30)

self.llistH = [None, None]

self.llistH[0] = QtGui.QComboBox()
self.llistH[0].resize(150, 30)
self.llistH[1] = QtGui.QComboBox()
self.llistH[1].resize(150, 30)


self.radio = QtGui.QVBoxLayout()
self.radio.addWidget(self.radioBtn_v)
self.radio.addWidget(self.radioBtn_h)

for marker in chunk.markers:
for ilist in self.llistV:
ilist.addItem(marker.label)
for ilist in self.llistH:
ilist.addItem(marker.label)


layout = QtGui.QGridLayout()   #creating layout
layout.setSpacing(10)
layout.addWidget(self.resTxt, 0, 0)
layout.addWidget(self.resEdt, 0, 1)

layout.addWidget(self.vectTxt, 1, 0)
layout.addWidget(self.vect2Txt, 2, 0)
layout.addWidget(self.llistH[0], 1, 1)
layout.addWidget(self.llistH[1], 1, 2)
layout.addWidget(self.llistV[0], 2, 1)
layout.addWidget(self.llistV[1], 2, 2)
layout.addWidget(self.Btn_cc, 0,2)
layout.addLayout(self.radio, 3, 0)

layout.addWidget(self.btnP1, 3, 1)
layout.addWidget(self.btnQuit, 3, 2)
self.setLayout(layout) 

proc_export = lambda : self.procExport()

QtCore.QObject.connect(self.btnP1, QtCore.SIGNAL("clicked()"), proc_export)
QtCore.QObject.connect(self.btnQuit, QtCore.SIGNAL("clicked()"), self, QtCore.SLOT("reject()"))

self.exec()

def procExport(self):

chunk = doc.chunk

path = PhotoScan.app.getExistingDirectory("Specify the folder with the projects:")
filelist = os.listdir(path)
doclist = list()
for file in filelist:
if file[-3:].upper() == "PSZ":
doclist.append(path + "\\" + file)


try:
d_x = d_y = float(self.resEdt.text())
except(ValueError):
PhotoScan.app.messageBox("Incorrect export resolution! Please use point delimiter.\n")
print("Script aborted.")
return 0

if self.llistV[0].currentIndex() == self.llistV[1].currentIndex():
PhotoScan.app.messageBox("Can't use the same marker for vector start and end.\n")
print("Script aborted.")
return 0

if self.llistH[0].currentIndex() == self.llistH[1].currentIndex():
PhotoScan.app.messageBox("Can't use the same marker for vector start and end.\n")
print("Script aborted.")
return 0

if len(chunk.markers) < 2:
PhotoScan.app.messageBox("No markers.\n")
print("Script aborted.")
return 0

print("Script started.")
type = "tif"
for doc_path in doclist:
doc.open(doc_path)
chunk = doc.chunk
T = chunk.transform.matrix


path = doc_path[:-3] + type


self.btnP1.setDisabled(True)
self.btnQuit.setDisabled(True)

m1 = getMarker(chunk, self.llistH[0].currentText())
m2 = getMarker(chunk, self.llistH[1].currentText())

horizontal = m2.position - m1.position
horizontal = T.mulv(horizontal)

m1 = getMarker(chunk, self.llistV[0].currentText())
m2 = getMarker(chunk, self.llistV[1].currentText())

vertical = m2.position - m1.position
vertical = T.mulv(vertical)

normal = vect(vertical, horizontal)

if self.radioBtn_h.isChecked():
vertical = vect(horizontal, normal)
horizontal = horizontal.normalized()

elif self.radioBtn_v.isChecked():
horizontal = -vect(vertical, normal)
#horizontal, vertical = -vertical, -horizontal

else:
PhotoScan.app.messageBox("Can't export orthophoto! " + path)
print("Script interrupted.")
continue

vertical.size = 4
vertical.w = 0
horizontal.size = 4
horizontal.w = 0
normal.size = 4
normal.w = 0

proj = PhotoScan.Matrix ([horizontal, vertical, -normal, PhotoScan.Vector([0,0,0,1])])

#print(proj) #debug
if self.Btn_cc.isChecked():
color_correction = color_correction=(True)

elif not self.Btn_cc.isChecked():
color_correction = color_correction=(False)

chunk.exportOrthophoto(path, type, self.blending, color_correction, projection = proj,write_world=True, dx = d_x, dy = d_y)
self.btnP1.setDisabled(False)
self.btnQuit.setDisabled(False)
PhotoScan.app.update()
print("Orthophoto exported to:\n" + path)

print("Script finished.")
return 1



def main():

global doc
doc = PhotoScan.app.document

app = QtGui.QApplication.instance()
parent = app.activeWindow()

dlg = ExportDlg(parent)

PhotoScan.app.addMenuItem("ArchaeoTools/Orthophoto", main)

15
Python and Java API / Re: Script for exporting orthophotos using markers
« on: September 25, 2015, 08:06:01 AM »
Hello Alexey,
I wanted to ask if you're maybe done with the script. This would be great because I need it quite urgently.
Thanks a lot!

Pages: [1] 2