Digital Aerial Photogrammetry

This section provides some basic recipes for performing photogrammetric operations with digital aerial imagery, using Geomatica’s python library.

In this section you will find recipes for ingesting aerial imagery, extracting Digital Elevation Models (DEMs) from multiple aerial images in a block and orthorectification.

Setup Digital Airphoto Project for Photogrammetric Processing

This recipe demonstrates how to setup an Orthoengine project for airphoto photogrammetric processing. Running this script will result in a digital airphoto project that is ready for DEM extraction and orthorectification.

Note: this script requires the user to provide the camera calibration parameters in a formatted camera calibration xml file.

Template Camera Calibration XML file

import os
import fnmatch
from pci.link import *
from pci.crproj import *
from pci.camimport import *
from pci.eoimport import *

raw_airphoto_dir = r"D:\AerialOrtho\photos"
input_files_list = []

for r, d, f in os.walk(raw_airphoto_dir):
    for file in fnmatch.filter(f, "*.tif"):
        print "found valid input file: ", file
        input_files_list.append(os.path.join(r, file))

# Create Link file
ingest_dir = r"D:\AerialOrtho\Ingest"
for raw_image in input_files_list:
    input_file = raw_image
    link_pix = os.path.join(ingest_dir, os.path.basename(os.path.splitext(raw_image)[0]) + ".pix")

    link(fili=input_file, filo=link_pix)

# Create OrthoEngine project file
mfile = os.path.join(ingest_dir, "*.pix")
oeproj = os.path.join(ingest_dir, "oe_initial.prj")

crproj(mfile=mfile, oeproj=oeproj, modeltyp="APDU", mapunits="UTM 17 D122")

# Import Camera Parameters
camfile = os.path.join(raw_airphoto_dir, "camera_calib.xml")
prepared_oe_proj = os.path.join(ingest_dir, "oe_configured.prj")

camimport(oeproji=oeproj, tfile=camfile, oeprojo=prepared_oe_proj)

# Import Exterior Orientation File
eofile = os.path.join(raw_airphoto_dir, "eo.txt")

eoimport(oeproji=prepared_oe_proj, tfile=eofile, eoformat=[3], mapunits="UTM 17 D122")

Automatic DEM Extraction from Aerial Imagery - Batch Processing

This recipe demonstrates how to extract a DEM from multiple Aerial images, using an OrthoEngine project that was previously setup for digital aerial image processing (See above recipe).

import os
import fnmatch
from pci.cpmmseg import cpmmseg
from pci.epipolar import epipolar
from pci.autodem import autodem
from pci.dsm2dtm import dsm2dtm

# Create math model from OE to ingested images
ingest_airphoto_dir = r"D:\AerialOrtho\Ingest"
output_dir = r"D:\AerialOrtho\AutoDEMBatch"
prepared_oe_proj = os.path.join(ingest_airphoto_dir, "oe_configured.prj")

ingest_image_list = []
for r, d, f in os.walk(ingest_airphoto_dir):
    for file in fnmatch.filter(f, "*.tif"):
        print "found valid input file: ", file
        ingest_image_list.append(os.path.join(r, file))

cpmmseg(oeproj=prepared_oe_proj)

# Generate Epipolar Images from Stereo pairs
epi_outdir = os.path.join(output_dir, 'epipolars')
if not os.path.exists(epi_outdir):
    os.makedirs(epi_outdir)

epipolar(mfile=ingest_airphoto_dir, dbic=[1, 2, 3], selmthd="max", outdir=epi_outdir, memsize=[1024])

# Extract Digital Surface Model (DSM) from multiple stereo airphoto images and mosaic them together
geocoded_dsm = os.path.join(output_dir, "airphoto_dsm.pix")

autodem(mfile=epi_outdir, dbic=[1], demdet='high', tertype='hilly', datatype='32R', extinter=[2], demedit='yes',
        outdir=output_dir, filedem=geocoded_dsm, mapunits='UTM 17 D122', pxszout=[0.9, 0.9])

# convert DSM to DEM (Automatically remove surface features such as trees and buildings from DSM)
dem_file = os.path.join(output_dir, "airphoto_dem.pix")
feature_size = [250]
gradient = [30]
remove_bumps = [9, 5, 9, 5]
fill_pits = [7, 5]
smooth_dem = [9]

dsm2dtm(fili=geocoded_dsm, dbic=[1], filo=dem_file, dboc=[1], objsize=feature_size, gthresh=gradient,
        tertype="hilly", bumpflt=remove_bumps, pitflt=fill_pits, medflt=smooth_dem)

Automatic Orthorectification of Aerial Imagery

This recipe provides a working example of how to orthorectify a single aerial image, using an OrthoEngine project that was previously setup for digital aerial image processing (See above recipe).

from pci.cpmmseg import cpmmseg
from pci.ortho import ortho

#Create math model from OE to ingested images
ingest_airphoto = r"C:\Aerial\Ingested\A17563-001.pix"
prepared_oe_proj = r"C:\Aerial\Ingested\oe_configured.prj"

# Copies the math model from the project file into a math model segment in FILE
cpmmseg(oeproj=prepared_oe_proj, file=ingest_airphoto)

#Orthorectify imagery with a user provided DEM
ortho_airphoto = r"C:\Aerial\Orthos\A17563-001.pix"
dem = r"C:\Aerial\DEM\1962_Saskatchewan_DEM.pix"

ortho(mfile=ingest_airphoto, filo=ortho_airphoto, ftype="PIX", bxpxsz="0.2", bypxsz="0.2", filedem=dem)

Automatic Orthorectification of Aerial Imagery - Batch Processing

This recipe provides a working example of how to orthorectify multiple aerial images, using an OrthoEngine project that was previously setup for digital aerial image processing (See above recipe).

import os, fnmatch
from pci.cpmmseg import cpmmseg
from pci.ortho import ortho

ingest_airphoto_dir = r"C:\Aerial\Ingested"
prepared_oe_proj = os.path.join(ingest_airphoto_dir, "oe_configured.prj")

# Copy math model from prepared orthoengine project file to all ingested images
# If the FILE parameter is not specified, all images contained within the project are copied.
cpmmseg(oeproj=prepared_oe_proj)

# Orthorectify imagery with a user provided DEM
ortho_dir = r"C:\Aerial\Orthos"
dem = r"C:\Aerial\dtm\PCI_Hamilton14_DTM_45cm.pix"

ortho(mfile=ingest_airphoto_dir, filo=ortho_dir, ftype="PIX", bxpxsz="0.2", bypxsz="0.2", filedem=dem)

Digital Aerial Orthorectification with user provided DEM - Full Workflow

This is a complete orthorectification workflow for digital aerial imagery (not including DEM extraction). This script combines multiple concepts from the previous recipes in this section.

import os
import fnmatch
from pci.link import *
from pci.crproj import *
from pci.camimport import *
from pci.eoimport import *
from pci.cpmmseg import cpmmseg
from pci.ortho import ortho

raw_airphoto_dir = r"D:\AerialOrtho\photos"

input_files_list = []

for r, d, f in os.walk(raw_airphoto_dir):
    for file in fnmatch.filter(f, "*.tif"):
        print "found valid input file: ", file
        input_files_list.append(os.path.join(r, file))

# Create Link file
ingest_dir = r"D:\AerialOrtho\ingest"
link_image_list = []
for raw_image in input_files_list:
    link_pix = os.path.join(ingest_dir, os.path.basename(os.path.splitext(raw_image)[0]) + ".pix")

    link(fili=raw_image, filo=link_pix)
    link_image_list.append(raw_image)

# Create OrthoEngine project file
mfile = os.path.join(ingest_dir, "*.pix")
oeproj = os.path.join(ingest_dir, "oe_initial.prj")

crproj(mfile=mfile, oeproj=oeproj, modeltyp="APDU", mapunits="UTM 17 D122")

# Import Camera Parameters
camfile = r"D:\AerialOrtho\camera\camera_calib.xml"
prepared_oe_proj = os.path.join(ingest_dir, "oe_configured.prj")

camimport(oeproji=oeproj, tfile=camfile, oeprojo=prepared_oe_proj)

# Import Exterior Orientation File
eofile = r"D:\AerialOrtho\eo\eo.txt"

eoimport(oeproji=prepared_oe_proj, tfile=eofile, eoformat=[3], mapunits="UTM 17 D122")

# Copy math model from prepared orthoengine project file to all ingested images
cpmmseg(oeproj=prepared_oe_proj)

# Orthorectify imagery with a user provided DEM
ortho_dir = r"D:\AerialOrtho\orthos"
dem = r"D:\AerialOrtho\dtm\PCI_Hamilton6_DTM_45cm.pix"

ortho(mfile=ingest_dir, filo=ortho_dir, ftype="PIX", bxpxsz="0.2", bypxsz="0.2", filedem=dem)

Digital Aerial Orthorectification with DEM Extraction - Full Workflow

This is a complete orthorectification workflow for digital aerial imagery (including DEM extraction). This script combines multiple concepts from the previous recipes in this section.

import os
import fnmatch
from pci.link import *
from pci.crproj import *
from pci.camimport import *
from pci.eoimport import *
from pci.cpmmseg import cpmmseg
from pci.epipolar import epipolar
from pci.autodem import autodem
from pci.dsm2dtm import dsm2dtm
from pci.ortho import ortho

raw_airphoto_dir = r"D:\AerialOrtho\photos"

input_files_list = []

for r, d, f in os.walk(raw_airphoto_dir):
    for file in fnmatch.filter(f, "*.tif"):
        print "found valid input file: ", file
        input_files_list.append(os.path.join(r, file))

# Create Link file
ingest_dir = r"D:\AerialOrtho\Ingest"
link_image_list = []
for raw_image in input_files_list:
    link_pix = os.path.join(ingest_dir, os.path.basename(os.path.splitext(raw_image)[0]) + ".pix")

    link(fili=raw_image, filo=link_pix)
    link_image_list.append(raw_image)

# Create OrthoEngine project file
mfile = os.path.join(ingest_dir, "*.pix")
oeproj = os.path.join(ingest_dir, "oe_initial.prj")

crproj(mfile=mfile, oeproj=oeproj, modeltyp="APDU", mapunits="UTM 17 D122")

# Import Camera Parameters
camfile = os.path.join(raw_airphoto_dir, "camera_calib.xml")
prepared_oe_proj = os.path.join(ingest_dir, "oe_configured.prj")

camimport(oeproji=oeproj, tfile=camfile, oeprojo=prepared_oe_proj)

# Import Exterior Orientation File
eofile = os.path.join(raw_airphoto_dir, "eo.txt")

eoimport(oeproji=prepared_oe_proj, tfile=eofile, eoformat=[3], mapunits="UTM 17 D122")

# Copy math model from prepared orthoengine project file to all ingested images
cpmmseg(oeproj=prepared_oe_proj)

#Generate Epipolar Images from Stereo pairs
epi_outdir = os.path.join(ingest_dir, 'epipolars')
if not os.path.exists(epi_outdir):
    os.makedirs(epi_outdir)

epipolar(mfile=ingest_dir, dbic=[3], selmthd="max", sampling=[1], outdir=epi_outdir)

#Extract Digital Surface Model (DSM) from multiple stereo airphoto images and mosaic them together
outdir = r"D:\AerialOrtho\OrthoDEM"
if not os.path.exists(outdir):
    os.makedirs(outdir)

geocoded_dsm = os.path.join(outdir, "airphoto_dsm.pix")

autodem(mfile=epi_outdir, outdir=outdir, filedem=geocoded_dsm, pxszout=[0.9, 0.9])

#convert DSM to DEM (Automatically remove surface features such as trees and buildings from DSM)
dem_file = os.path.join(outdir, "airphoto_dem.pix")

dsm2dtm(fili=geocoded_dsm, dbic=[1], filo=dem_file, dboc=[1], objsize=[150], gthresh=[35], tertype="hilly",
        bumpflt=[9, 5, 9, 5], pitflt=[7, 5], medflt=[9])

#Orthorectify imagery with a user provided DEM

ortho(mfile=ingest_dir, filo=outdir, ftype="PIX", bxpxsz="0.2", bypxsz="0.2", filedem=dem_file)

Create Camera Calibration (.xml) file

This recipe demonstrates how to quickly create a camera calibration file in the XML format required by Geomatica’s automated aerial processing algorithms.

In most cases it will be easier to acquire a template of the XML file (See link below) and edit it manually. However, this script provides an example of a logical process for creating a GUI that accepts the user inputs required for creating the camera calibration xml file in a user-friendly fashion; it limits the input fields based on the sensor type.

Template Camera Calibration XML file

import os

#Set camera calibration location
cam_dir = raw_input("Please provide the directory for the Camera Calibration file: ")
cam_file = cam_dir + os.sep + "camera_calib.xml"
target = open(cam_file, 'w+')

#Get Camera Calibration Values
x = 0
while x == 0:
    cam_sensor = raw_input("What Camera Sensor are you using (UltraCam/DMC/Other)? ")

    if cam_sensor.lower() == "ultracam":
        print "Progressing to UltraCam setup mode..."
        x = 1
    elif cam_sensor.lower() == "dmc":
        print "Progressing to DMC setup mode..."
        x = 1
    elif cam_sensor.lower() == "other":
        print "Progressing to generic camera setup mode..."
        x = 1
    else:
        print "Your answer could not be verified. Please respond with: ultracam, dmc or other"
        x = 0

if cam_sensor.lower() == "ultracam":
    focal_length = raw_input("Please provide the focal length in mm: ")
    sym_ppx =  raw_input("Please provide the principal Point offset x: ")
    sym_ppy =  raw_input("Please provide the principal Point offset y: ")
    chip_width = raw_input("Please provide the chip size width in mm: ")
    chip_length = raw_input("Please provide the chip size length in mm: ")
elif cam_sensor.lower() == "dmc":
    focal_length = raw_input("Please provide the focal length in mm: ")
    chip_width = raw_input("Please provide the chip size width in mm: ")
    chip_length = raw_input("Please provide the chip size length in mm: ")
else:
    focal_length = raw_input("Please provide the focal length in mm: ")
    sym_ppx =  raw_input("Please provide the principal Point offset x: ")
    sym_ppy =  raw_input("Please provide the principal Point offset y: ")
    chip_width = raw_input("Please provide the chip size width in mm: ")
    chip_length = raw_input("Please provide the chip size length in mm: ")

    radial_distortion = raw_input("Do you have radial distortion values? (y/n)")
    if radial_distortion == "y":
        rldc_r0 = raw_input("please provide RLDC R0 value: ")
        rldc_r1 = raw_input("please provide RLDC R1 value: ")
        rldc_r2 = raw_input("please provide RLDC R2 value: ")
        rldc_r3 = raw_input("please provide RLDC R3 value: ")
        rldc_r4 = raw_input("please provide RLDC R4 value: ")
        rldc_r5 = raw_input("please provide RLDC R5 value: ")
        rldc_r6 = raw_input("please provide RLDC R6 value: ")
        rldc_r7 = raw_input("please provide RLDC R7 value: ")
    else:
        print "Setting radial coefficients to 0 (zero)"
        rldc_r0 = 0
        rldc_r1 = 0
        rldc_r2 = 0
        rldc_r3 = 0
        rldc_r4 = 0
        rldc_r5 = 0
        rldc_r6 = 0
        rldc_r7 = 0

    decentering_distortion = raw_input("Do you have decentering distortion values? (y/n)")
    if decentering_distortion == "y":
        ddc_p1 = raw_input("please provide DDC P1 value: ")
        ddc_p2 = raw_input("please provide DDC P2 value: ")
        ddc_p3 = raw_input("please provide DDC P3 value: ")
        ddc_p4 = raw_input("please provide DDC P4 value: ")
    else:
        print "Setting decentering coefficients to 0 (zero)"
        ddc_p1 = 0
        ddc_p2 = 0
        ddc_p3 = 0
        ddc_p4 = 0

#Create Camera Calibration XML file

target.write("<CAMERA NAME=\"" + cam_sensor.upper() + "\">\n")
target.write("<LENS_SERIAL/>\n")
target.write("<CALIB_SOURCE/>\n")
target.write("<CALIB_DATE/>\n")
target.write("<FL>" + str(focal_length) + "</FL>\n")
target.write("<SYMMETRY PPX=\"" + str(sym_ppx) + "\" PPY=\"" + str(sym_ppy) + "\"/>\n")
target.write("<RLDC name=\"R0\">" + str(rldc_r0) + "</RLDC>\n")
target.write("<RLDC name=\"R1\">" + str(rldc_r1) + "</RLDC>\n")
target.write("<RLDC name=\"R2\">" + str(rldc_r2) + "</RLDC>\n")
target.write("<RLDC name=\"R3\">" + str(rldc_r3) + "</RLDC>\n")
target.write("<RLDC name=\"R4\">" + str(rldc_r4) + "</RLDC>\n")
target.write("<RLDC name=\"R5\">" + str(rldc_r5) + "</RLDC>\n")
target.write("<RLDC name=\"R6\">" + str(rldc_r6) + "</RLDC>\n")
target.write("<RLDC name=\"R7\">" + str(rldc_r7) + "</RLDC>\n")
target.write("<DDC name=\"P1\">" + str(ddc_p1) + "</DDC>\n")
target.write("<DDC name=\"P2\">" + str(ddc_p2) + "</DDC>\n")
target.write("<DDC name=\"P3\">" + str(ddc_p3) + "</DDC>\n")
target.write("<DDC name=\"P4\">" + str(ddc_p4) + "</DDC>\n")
target.write("<DIGITAL>\n")
target.write("<CHIP_SIZE WIDTH=\"" + str(chip_width) + "\" HEIGHT=\"" + str(chip_length) + "\"/>\n")
target.write("<YSCALE>1.000</YSCALE>\n")
target.write("</DIGITAL>\n")
target.write("</CAMERA>")

print ""
print "completed writing Camera Calibration file"
print ""
print "Camera calibration file located here: " + cam_file