Forum

Author Topic: Example code for full processin chain  (Read 34117 times)

jmsuomal

  • Newbie
  • *
  • Posts: 17
    • View Profile
Example code for full processin chain
« on: January 08, 2014, 04:00:08 PM »
Hi,

I am integrating PhotoScan to be a part of a larger processing chain that is based around a Matlab processor engine. The engine is already now producing/organizing all PhotoScan input files and using again the output files. Until now, processing in PhotoScan has required a lot of manual clicking including risks for user errors and producing lots of variation in outputs.

My goal was to generate with the other PhotoScan input files also a ready script file so that user would need to do only minimal (if any) clicking in PhotoScan. I have just spent better part of two working days trying to learn Python and how to do all that scripting in PhotoScan. Now I am happy to tell that I managed to get it to work.  :)

The only thing that bugs me was that I was not able to find that good examples for this complete code, but I had to do combine it from multiple small code snippets in this forum and in the Python Reference Manual, and still add a LOT of my own head scratching in. For one thing, I would have appreciated hugely if the official Python reference manual would have contained a complete "example script" appendix with practical example how to do this all. As I found this lack of complete examples annoying/very time consuming, I now wanted to share my script with rest of the community:

Code: [Select]
# Python script for automated PhotoScan processing
# PhotoScan version 1.0.0

# DEFINE PROCESSING SETTINGS
print("---Defining processing settings...")

# Define: Home directory (later this is referred as ".")
HomeDirectory = "D:\\Temp\\PhotoScanScriptTest"

# Define: Camera calib file
CalibFile = "D:\\Temp\\AgisoftLensCameraCalib.xml"

# Define: PhotoScan Project File
PhotoScanProjectFile = ".\\PhotoScan_Project.psz"

# Define: Source images
AerialImagesPattern = ".\\Aerial_Images_8bit_tif\\*.tif"

# Define: Orientation file
OrientationFile = ".\\CameraOrientations_IMU.txt"

# Define: Cameras output file
CamerasOPKFile = ".\\CameraOrientations_PhotoScan.opk"

# Define: Coordinate system
CoordinateSystemEPSG = "EPSG::32632"

# Define: DSMResolutions (in meters),
# 0 == GSD resolution
DSMResolutions = [1.00, 0]

# Define: OrthoImageResolutions (in meters)
# 0 == GSD resolution
OrthoImageResolutions = [1.00, 0]

# Define: AlignPhotosAccuracy ["high", "medium", "low"]
AlignPhotosAccuracy = "medium"

# Define: BuildGeometryQuality ["lowest", "low", "medium", "high", "ultra"]
BuildGeometryQuality = "medium"

# Define: BuildGeometryFaces [’low’, ‘medium’, ‘high’] or exact number.
BuildGeometryFaces = "medium"



# START ACTUAL SCRIPT (DO NOT EDIT ANYTHING BELOW THIS!)

# INIT ENVIRONMENT
# import stuff
import os
import glob
import PhotoScan
import math

# Set home folder
os.chdir(HomeDirectory)
print("Home directory: " + HomeDirectory )

# get main app objects
doc = PhotoScan.app.document
app = PhotoScan.Application()

# create chunk
chunk = PhotoScan.Chunk()
chunk.label = "New_Chunk"
doc.chunks.add(chunk)

# SET COORDINATE SYSTEM
print("---Settings coordinate system...")
# init coordinate system object
CoordinateSystem = PhotoScan.CoordinateSystem()
if not(CoordinateSystem.init(CoordinateSystemEPSG)):
   app.messageBox("Coordinate system EPSG code not recognized!")
# define coordinate system in chunk
chunk.crs = CoordinateSystem
chunk.projection = CoordinateSystem

# FIND ALL PHOTOS IN PATH
AerialImageFiles = glob.glob(AerialImagesPattern)

# LOAD CAMERA CALIBRATION
print("---Loading camera calibration...")
# we need to open first image to define image size
cam = PhotoScan.Camera()
cam.open(AerialImageFiles[0])
# Load sensor calib
user_calib = PhotoScan.Calibration()
if not(user_calib.load(CalibFile)):
   app.messageBox("Loading of calibration file failed!")
# build sensor object
sensor = PhotoScan.Sensor()
sensor.label = "MyCamera"
sensor.width = cam.width
sensor.height = cam.height
sensor.fixed = False
sensor.user_calib = user_calib
chunk.sensors.add(sensor)

# LOAD AERIAL IMAGES
print("---Loading images...")
# load each image
for FileName in AerialImageFiles:
   print("File: " + FileName)
   cam = PhotoScan.Camera()
   if not(cam.open(FileName)):
      app.messageBox("Loading of image failed: " + FileName)
   cam.label = cam.path.rsplit("/",1)[1]
   cam.sensor = sensor
   chunk.cameras.add(cam)

# LOAD CAMERA ORIENTATIONS
print("---Loading initial photo orientations...")
print("File: " + OrientationFile)
# build ground control
chunk.ground_control.projection = CoordinateSystem
chunk.ground_control.crs = CoordinateSystem
if not(chunk.ground_control.load(OrientationFile,"csv")):
   app.messageBox("Loading of orientation file failed!")
chunk.ground_control.apply()

# SAVE PROJECT
print("---Saving project...")
print("File: " + PhotoScanProjectFile)
if not(doc.save(PhotoScanProjectFile)):
   app.messageBox("Saving project failed!")

# All init done! Ready to start processing.

# ALIGN PHOTOS
print("---Aligning photos ...")
print("Accuracy: " + AlignPhotosAccuracy)
chunk.matchPhotos(accuracy=AlignPhotosAccuracy, preselection="ground control")
chunk.alignPhotos()

# SAVE PROJECT
print("---Saving project...")
print("File: " + PhotoScanProjectFile)
if not(doc.save(PhotoScanProjectFile)):
   app.messageBox("Saving project failed!")

# If ground control markers are used, code should stop here and user should click them in now

# BUILD GEOMETRY
print("---Building Geometry...")
print("Quality: " + BuildGeometryQuality)
print("Faces: " + str(BuildGeometryFaces))
# THESE ROWS WERE ALTERED IN v1.0.0 UPDATE
# chunk.buildDepth(quality=BuildGeometryQuality)
# chunk.buildModel(object="height field", geometry="smooth", faces=BuildGeometryFaces)
if not(chunk.buildDenseCloud(quality=BuildGeometryQuality)):
   app.messageBox("Builde Dense Cloud failed!")

if not(chunk.buildModel(surface="height field", source="dense", interpolation="enabled", faces=BuildGeometryFaces)):
   app.messageBox("Build mesh model failed!")

# SAVE PROJECT
print("---Saving project...")
print("File: " + PhotoScanProjectFile)
if not(doc.save(PhotoScanProjectFile)):
   app.messageBox("Saving project failed!")

# Actual processing done
# EXPORT DATA:

# EXPORT CAMERAS
print("---Exporting camera positions...")
if not(chunk.exportCameras(CamerasOPKFile, "opk", chunk.projection)):
   app.messageBox("Exporting Cameras OPK failed!")

# CALCULATE THE BEST SENSIBLE PIXEL SIZES
Resolution = 0;
#if Resolution == 0:
# Get average camera altitude and focal length
N_cameras = len(chunk.cameras)
CamAlt = 0.0
focal_length = 0.0
for i in range(N_cameras):
   CamAlt += chunk.ground_control.locations[chunk.cameras[i]].coord[2]
   focal_length += (chunk.cameras[i].sensor.calibration.fx + chunk.cameras[0].sensor.calibration.fy) / 2

CamAlt /= N_cameras
focal_length /= N_cameras
# Get average ground  altitude
N_points = len(chunk.point_cloud.points)
GroundAlt = 0.0
for i in range(N_points):
   GroundAlt += chunk.point_cloud.points[i].coord.z

GroundAlt = GroundAlt / N_points
# calculate Ground sampling distance
GroundSamplingDistance = math.fabs(CamAlt-GroundAlt) / focal_length
# round it to next millimeter
GroundSamplingDistance = math.ceil(GroundSamplingDistance * 1000)/1000

# EXPORT DSM(S)
print("---Exporting Digital Surface Model(s)...")
for Resolution in DSMResolutions:
   if Resolution == 0:
      Resolution = GroundSamplingDistance
   
   FileName = ".\\DSM_PixelSize=" + str(Resolution) + "m.tif"
   print("File: " + FileName)
   if not(chunk.exportDem(FileName, format="tif", dx=Resolution, dy=Resolution, projection=chunk.projection)):
      app.messageBox("Exporting DSM failed: " + FileName)
   

# EXPORT ORTHOIMAGE(S)
print("---Exporting Orthoimage(s)...")
for Resolution in OrthoImageResolutions:
   if Resolution == 0:
      Resolution = GroundSamplingDistance
   
   FileName = ".\\OrthoImage_PixelSize=" + str(Resolution) + "m.tif"
   print("File: " + FileName)
   if not(chunk.exportOrthophoto(FileName, format="tif", projection=chunk.projection, blending="mosaic", dx=Resolution, dy=Resolution)):
      app.messageBox("Exporting orthoimage failed: " + FileName)
   
# SAVE PROJECT
print("---Saving project...")
print("File: " + PhotoScanProjectFile)
if not(doc.save(PhotoScanProjectFile)):
   app.messageBox("Saving project failed!")

# Close photoscan
# app.quit()
Instructions for fellow Python-virgins: copy-paste the script to notepad, edit the files/folders to your own ones, save file as "PythoScript.py" (or anything you want), and run it in PhotoScan using "Tools>Run Script..."

And here is an example of the CameraOrientations_IMU.txt file:
Code: [Select]
#CoordinateSystem: WGS84_UTM_Zone_32N(EPSG:32632)
#FileName EastingX NorthingY Altitude
P1440733_8bit.tif 304323.409 5740634.830 79.841
P1440734_8bit.tif 304323.758 5740635.029 79.597
P1440735_8bit.tif 304322.964 5740633.880 78.496
P1440736_8bit.tif 304322.164 5740632.638 79.890

Alexey:
(1) If you want, feel free to add any parts of this script to the Python Reference manual.
(2) Also as requested already in an earlier topic, I would love to be able to run this script from OS command line for example with syntax:

   > photoscan.exe -RunScript "D:\Temp\PhotoScanScriptTest\PythonScript.py"

...and preferably even with option to disable/hide whole user interface (so that there is no chance that the user disturbs the running of the script, for example by pressing cancel in some dialog):

   > photoscan.exe -RunScriptHidden "D:\Temp\PhotoScanScriptTest\PythonScript.py"

andyroo

  • Sr. Member
  • ****
  • Posts: 438
    • View Profile
Re: Example code for full processin chain
« Reply #1 on: January 22, 2014, 02:18:18 AM »
Hi jmsuomal!

Thank you very much for that post. I muddle through python and haven't had the time to build a script to reprocess my earlier imagery with new settings/versions. Your script is the best starting point I've seen yet.

Andy

Mfranquelo

  • Full Member
  • ***
  • Posts: 171
    • View Profile
Re: Example code for full processin chain
« Reply #2 on: January 22, 2014, 05:47:48 PM »
Thats brilliant jmsuomal.

andy_s

  • Full Member
  • ***
  • Posts: 147
    • View Profile
Re: Example code for full processin chain
« Reply #3 on: January 23, 2014, 12:40:20 PM »
Yes jmsuomal,

thanks a lot for posting this.

jmsuomal

  • Newbie
  • *
  • Posts: 17
    • View Profile
Re: Example code for full processin chain
« Reply #4 on: January 23, 2014, 01:04:21 PM »
With "Captured"'s hint, I also created a workaround how to run (this) Python script in PhotoScan from command line syntax:

http://www.agisoft.ru/forum/index.php?topic=1843.0


MINE

  • Newbie
  • *
  • Posts: 16
    • View Profile
Re: Example code for full processin chain
« Reply #5 on: June 25, 2014, 12:10:56 PM »
Hi,

Thanks for the code you made, it's great!

I just have a problem with loading the camera orientations, it simply doesn't. I attached the file I'm trying to use. I also tried using space or tab in stead of commas, but it just doesn't load anything.

Does anyone have a solution to this?
« Last Edit: June 25, 2014, 12:30:33 PM by MINE »

Oliver

  • Newbie
  • *
  • Posts: 2
    • View Profile
Re: Example code for full processin chain
« Reply #6 on: August 29, 2014, 04:36:21 PM »
When i try to start this script on startup like in Thread: http://www.agisoft.ru/forum/index.php?topic=1843.0

I get the following error:
doc.chunks.add(chunk)
AttributeError: 'NoneType' object has no attribute 'chunks'

I think its different to start it from the RunScript and to copy it to the scripts folder or is there a way to get this script working on startup ?

Regards
Oliver

Oliver

  • Newbie
  • *
  • Posts: 2
    • View Profile
Re: Example code for full processin chain
« Reply #7 on: August 29, 2014, 07:17:55 PM »
Hi all again,
just a quick remark the script works fine when i load it with Runscript for the program
but its not working when i copy it to the script folder in the local Directory.  I think the script is executed
when something is not initialized while the startup process. Is there a workaround ?

Regards
Oliver

Alexey Pasumansky

  • Agisoft Technical Support
  • Hero Member
  • *****
  • Posts: 14813
    • View Profile
Re: Example code for full processin chain
« Reply #8 on: August 29, 2014, 08:41:54 PM »
Hello Oliver,

It has been already discussed: http://www.agisoft.ru/forum/index.php?topic=2364

Starting automatic processing chain by the use of auto-run script is blocked by the reason.
Best regards,
Alexey Pasumansky,
Agisoft LLC

beerockxs2

  • Newbie
  • *
  • Posts: 6
    • View Profile
Re: Example code for full processin chain
« Reply #9 on: November 12, 2014, 04:55:15 PM »
Hello Oliver,

It has been already discussed: http://www.agisoft.ru/forum/index.php?topic=2364

Starting automatic processing chain by the use of auto-run script is blocked by the reason.
Is there a specific reason why you want to block the automated scripting? There's always the option of using something like AutoIt to automate the process, it just adds another hassle for us users.
« Last Edit: November 12, 2014, 06:40:07 PM by beerockxs2 »

tforward2

  • Newbie
  • *
  • Posts: 17
    • View Profile
Re: Example code for full processin chain
« Reply #10 on: November 15, 2014, 08:43:09 AM »
One question I have is how do you deal with the initial chunking of the data. You load all images into the tool at once however how do you deal with processing 10000 images at 14 mb per image? If you try to load that many images into the tool you'll have to have one hell of a rig to handle all that data. Are you manually selecting what photos get passed to the tool?

Alexey Pasumansky

  • Agisoft Technical Support
  • Hero Member
  • *****
  • Posts: 14813
    • View Profile
Re: Example code for full processin chain
« Reply #11 on: November 15, 2014, 05:50:02 PM »
Hello tforward2,

If you need to split the initial dataset into chunks, you can try to do it according to the image center coordinates.
Best regards,
Alexey Pasumansky,
Agisoft LLC

pap014

  • Newbie
  • *
  • Posts: 12
    • View Profile
Re: Example code for full processin chain
« Reply #12 on: August 18, 2015, 12:38:46 PM »
Hello, whenever i want to use Jmsuomal's script it gives me this error :

 ---Defining processing settings...
Home directory: E:\IMV\Projets [Calculs]\P-A 2015\projets\C_01etampes\test2\photoscan
Traceback (most recent call last):
  File "E:/IMV/Projets [Calculs]/P-A 2015/projets/C_01etampes/test2/photoscan/modèle1.py", line 67, in <module>
AttributeError: 'list' object has no attribute 'add'

related to this part :

chunk = PhotoScan.Chunk()
chunk.label = "New_Chunk"
doc.chunks.add(chunk)

Can i do something to resolve that problem ? thank you very much

Alexey Pasumansky

  • Agisoft Technical Support
  • Hero Member
  • *****
  • Posts: 14813
    • View Profile
Re: Example code for full processin chain
« Reply #13 on: August 19, 2015, 11:44:19 AM »
Hello pap014,

Seems that you are using old script for the newer version. You can do the following:

Code: [Select]
chunk = doc.addChunk()
chunk.label = "New Chunk"
Best regards,
Alexey Pasumansky,
Agisoft LLC

pap014

  • Newbie
  • *
  • Posts: 12
    • View Profile
Re: Example code for full processin chain
« Reply #14 on: August 19, 2015, 04:11:30 PM »
Thank you so much for your help, it now gives me a new error

 Traceback (most recent call last):
  File "E:/IMV/Projets [Calculs]/P-A 2015/projets/C_01etampes/test2/photoscan/modèle1.py", line 120, in <module>
ValueError: wrong crs definition string

 line 120 : CoordinateSystem = PhotoScan.CoordinateSystem()

If you have a simple answer i would be glad to have it, i'm also trying to understand by myself.

thank you very much