Update code to nearly comply with PEP8

pull/159/head
Dakota Benjamin 2015-08-27 08:59:35 -04:00
rodzic 05c006a655
commit e966c411dc
1 zmienionych plików z 388 dodań i 327 usunięć

551
run.py 100644 → 100755
Wyświetl plik

@ -1,214 +1,232 @@
#!/usr/bin/python #!/usr/bin/python
import os, sys, multiprocessing, json, datetime, re, subprocess, shutil, shlex, collections, fractions, argparse import os
import sys
import multiprocessing
import json
import datetime
import re
import subprocess
import shutil
import shlex
# import collections # Never used
import fractions
import argparse
import knnMatch_exif import knnMatch_exif
## the defs
# the defs
CURRENT_DIR = os.getcwd() CURRENT_DIR = os.getcwd()
BIN_PATH_ABS = os.path.abspath(os.path.dirname(os.path.abspath(__file__))) BIN_PATH_ABS = os.path.abspath(os.path.dirname(os.path.abspath(__file__)))
CORES = multiprocessing.cpu_count() CORES = multiprocessing.cpu_count()
def getCcdWidths(): def get_ccd_widths():
with open(BIN_PATH_ABS + "/ccd_defs.json") as jsonFile: """Return the CCD Width of the camera listed in the JSON defs file."""
with open(BIN_PATH_ABS + '/ccd_defs.json') as jsonFile:
return json.load(jsonFile) return json.load(jsonFile)
objects = [] objects = []
ccdWidths = getCcdWidths() ccdWidths = get_ccd_widths()
BIN_PATH = BIN_PATH_ABS + "/bin" BIN_PATH = BIN_PATH_ABS + '/bin'
objectStats = {
'count': 0, 'good': 0, 'bad': 0, 'minWidth': 0, 'minHeight': 0,
'maxWidth': 0, 'maxHeight': 0
}
objectStats = {'count': 0, "good": 0, "bad": 0, "minWidth": 0, "minHeight": 0, "maxWidth": 0, "maxHeight": 0} jobOptions = {'resizeTo': 0, 'srcDir': CURRENT_DIR, 'utmZone': -999,
jobOptions = {'resizeTo': 0, 'srcDir': CURRENT_DIR, 'utmZone': -999, 'utmSouth': False, 'utmEastOffset': 0, 'utmNorthOffset': 0} 'utmSouth': False, 'utmEastOffset': 0, 'utmNorthOffset': 0}
# parse arguments # parse arguments
processopts = ['resize', 'getKeypoints', 'match', 'bundler', 'cmvs', 'pmvs',
'odm_meshing', 'odm_texturing', 'odm_georeferencing',
'odm_orthophoto']
parser = argparse.ArgumentParser(description='OpenDroneMap') parser = argparse.ArgumentParser(description='OpenDroneMap')
parser.add_argument('--resize-to', '-r', #currently doesn't support 'orig' parser.add_argument('--resize-to', # currently doesn't support 'orig'
metavar = '<integer>', metavar='<integer>',
default = 2400, default=2400,
type = int, type=int,
help = 'resizes images by the largest side') help='resizes images by the largest side')
parser.add_argument('--start-with', '-s', parser.add_argument('--start-with', '-s',
metavar = '<string>', metavar='<string>',
default = 'resize', default='resize',
choices = ['resize', 'getKeypoints', 'match', 'bundler', choices=processopts,
'cmvs', 'pmvs', 'odm_meshing', 'odm_texturing', help=('Can be one of: ' + ' | '.join(processopts)))
'odm_georeferencing', 'odm_orthophoto'],
help = 'can be one of: resize getKeypoints match bundler \
cmvs pmvs odm_meshing odm_texturing odm_georeferencing \
odm_orthophoto')
parser.add_argument('--end-with', '-e', parser.add_argument('--end-with', '-e',
metavar = '<string>', metavar='<string>',
default = 'odm_orthophoto', default='odm_orthophoto',
choices = ['resize', 'getKeypoints', 'match', 'bundler', choices=processopts,
'cmvs', 'pmvs', 'odm_meshing', 'odm_texturing', help=('Can be one of:' + ' | '.join(processopts)))
'odm_georeferencing', 'odm_orthophoto'],
help = 'can be one of: resize getKeypoints match bundler \
cmvs pmvs odm_meshing odm_texturing odm_georeferencing \
odm_orthophoto')
parser.add_argument('--run-only', parser.add_argument('--run-only',
metavar = '<string>', metavar='<string>',
choices = ['resize', 'getKeypoints', 'match', 'bundler', choices=processopts,
'cmvs', 'pmvs', 'odm_meshing', 'odm_texturing', help=('Can be one of:' + ' | '.join(processopts)))
'odm_georeferencing', 'odm_orthophoto'],
help = 'can be one of: resize getKeypoints match bundler \
cmvs pmvs odm_meshing odm_texturing odm_georeferencing \
odm_orthophoto')
parser.add_argument('--force-focal', parser.add_argument('--force-focal',
metavar = '<positive float>', metavar='<positive float>',
type = float, type=float,
help = 'Override the focal length information \ help=('Override the focal length information for the '
for the images') 'images'))
parser.add_argument('--force-ccd', parser.add_argument('--force-ccd',
metavar = '<positive float>', metavar='<positive float>',
type = float, type=float,
help = 'Override the ccd width information for the images') help='Override the ccd width information for the images')
parser.add_argument('--matcher-threshold', parser.add_argument('--matcher-threshold',
metavar = '<percent>', metavar='<percent>',
default = 2.0, default=2.0,
type = float, type=float,
help = 'Ignore matched keypoints if the two images share \ help=('Ignore matched keypoints if the two images share '
less than <float> percent of keypoints') 'less than <float> percent of keypoints'))
parser.add_argument('--matcher-ratio', parser.add_argument('--matcher-ratio',
metavar = '<float>', metavar='<float>',
default = 0.6, default=0.6,
type = float, type=float,
help = 'Ratio of the distance to the next best matched \ help=('Ratio of the distance to the next best matched '
keypoint') 'keypoint'))
parser.add_argument('--matcher-preselect', parser.add_argument('--matcher-preselect',
type = bool, type=bool,
default = True, metavar='',
help = 'use GPS exif data, if available, to match each \ default=True,
image only with its k-nearest neighbors, or all images \ help=('use GPS exif data, if available, to match each '
within a certain distance threshold') 'image only with its k-nearest neighbors, or all '
'images within a certain distance threshold'))
parser.add_argument('--matcher-useKnn', parser.add_argument('--matcher-useKnn',
type = bool, type=bool,
default = True, metavar='',
help = 'use GPS exif data, if available, to match each \ default=True,
image only with its k-nearest neighbors, or all images \ help=('use GPS exif data, if available, to match each '
within a certain distance threshold') 'image only with its k-nearest neighbors, or all '
'images within a certain distance threshold'))
parser.add_argument('--matcher-kDistance', parser.add_argument('--matcher-kDistance',
metavar = '<integer>', metavar='<integer>',
default = 20, default=20,
type = int, type=int,
help = 'The maximum number of images per cluster') help='')
parser.add_argument('--cmvs-maxImages', parser.add_argument('--cmvs-maxImages',
metavar = '<integer>', metavar='<integer>',
default = 500, default=500,
type = int, type=int,
help = 'The maximum number of images per cluster') help='The maximum number of images per cluster')
parser.add_argument('--pmvs-level', parser.add_argument('--pmvs-level',
metavar = '<positive integer>', metavar='<positive integer>',
default = 1, default=1,
type = int, type=int,
help = 'The level in the image pyramid that is used \ help=('The level in the image pyramid that is used '
for the computation. see \ 'for the computation. see '
http://www.di.ens.fr/pmvs/documentation.html for \ 'http://www.di.ens.fr/pmvs/documentation.html for '
more pmvs documentation') 'more pmvs documentation'))
parser.add_argument('--pmvs-csize', parser.add_argument('--pmvs-csize',
metavar = '< positive integer>', metavar='< positive integer>',
default = 2, default=2,
type = int, type=int,
help = 'Cell size controls the density of reconstructions') help='Cell size controls the density of reconstructions')
parser.add_argument('--pmvs-threshold', parser.add_argument('--pmvs-threshold',
metavar = '<float: -1.0 <= x <= 1.0>', metavar='<float: -1.0 <= x <= 1.0>',
default = 0.7, default=0.7,
type = float, type=float,
help = 'A patch reconstruction is accepted as a success \ help=('A patch reconstruction is accepted as a success '
and kept, if its associcated photometric consistency \ 'and kept, if its associcated photometric consistency '
measure is above this threshold.') 'measure is above this threshold.'))
parser.add_argument('--pmvs-wsize', parser.add_argument('--pmvs-wsize',
metavar = '<positive integer>', metavar='<positive integer>',
default = 7, default=7,
type = int, type=int,
help = 'pmvs samples wsize x wsize pixel colors from \ help=('pmvs samples wsize x wsize pixel colors from '
each image to compute photometric consistency score. \ 'each image to compute photometric consistency '
For example, when wsize=7, 7x7=49 pixel colors are \ 'score. For example, when wsize=7, 7x7=49 pixel '
sampled in each image. Increasing the value leads to \ 'colors are sampled in each image. Increasing the '
more stable reconstructions, but the program becomes \ 'value leads to more stable reconstructions, but '
slower.') 'the program becomes slower.'))
parser.add_argument('--pmvs-minImageNum', parser.add_argument('--pmvs-minImageNum',
metavar = '<positive integer>', metavar='<positive integer>',
default = 3, default=3,
type = int, type=int,
help = 'Each 3D point must be visible in at least \ help=('Each 3D point must be visible in at least '
minImageNum images for being reconstructed. 3 is \ 'minImageNum images for being reconstructed. 3 is '
suggested in general.') 'suggested in general.'))
parser.add_argument('--odm_meshing-maxVertexCount', parser.add_argument('--odm_meshing-maxVertexCount',
metavar = '<positive integer>', metavar='<positive integer>',
default = 100000, default=100000,
type = int, type=int,
help = 'The maximum vertex count of the output mesh') help='The maximum vertex count of the output mesh')
parser.add_argument('--odm_meshing-octreeDepth', parser.add_argument('--odm_meshing-octreeDepth',
metavar = '<positive integer>', metavar='<positive integer>',
default = 9, default=9,
type = int, type=int,
help = 'Oct-tree depth used in the mesh reconstruction, \ help=('Oct-tree depth used in the mesh reconstruction, '
increase to get more vertices, recommended values are \ 'increase to get more vertices, recommended '
8-12') 'values are 8-12'))
parser.add_argument('--odm_meshing-samplesPerNode', parser.add_argument('--odm_meshing-samplesPerNode',
metavar = '<float >= 1.0>', metavar='<float >= 1.0>',
default = 1, default=1,
type = float, type=float,
help = 'Number of points per octree node, recommended \ help=('Number of points per octree node, recommended '
value: 1.0') 'value: 1.0'))
parser.add_argument('--odm_meshing-solverDivide', parser.add_argument('--odm_meshing-solverDivide',
metavar = '<positive integer>', metavar='<positive integer>',
default = 9, default=9,
type = int, type=int,
help = 'Oct-tree depth at which the Laplacian equation \ help=('Oct-tree depth at which the Laplacian equation '
is solved in the surface reconstruction step. \ 'is solved in the surface reconstruction step. '
Increasing this value increases computation times \ 'Increasing this value increases computation '
slightly but helps reduce memory usage.') 'times slightly but helps reduce memory usage.'))
parser.add_argument('--odm_texturing-textureResolution', parser.add_argument('--odm_texturing-textureResolution',
metavar = '<positive integer>', metavar='<positive integer>',
default = 4096, default=4096,
type = int, type=int,
help = 'The resolution of the output textures. Must be \ help=('The resolution of the output textures. Must be '
greater than textureWithSize.') 'greater than textureWithSize.'))
parser.add_argument('--odm_texturing-textureWithSize', parser.add_argument('--odm_texturing-textureWithSize',
metavar = '<positive integer>', metavar='<positive integer>',
default = 3600, default=3600,
type = int, type=int,
help = 'The resolution to rescale the images performing \ help=('The resolution to rescale the images performing '
the texturing.') 'the texturing.'))
parser.add_argument('--odm_georeferencing-useGcp',
metavar='<True | False>',
default=True,
type=bool,
help=('Skip reading the GCP file and use EXIF data to '
'extract geographical coordinates for generating '
'an orthophoto.'))
parser.add_argument('--odm_georeferencing-gcpFile', parser.add_argument('--odm_georeferencing-gcpFile',
metavar = '<path string>', metavar='<path string>',
default = 'gcp_list.txt', default='gcp_list.txt',
help = 'path to the file containing the ground control \ help=('path to the file containing the ground control '
points used for georeferencing.The file needs to be on \ 'points used for georeferencing.The file needs to '
the following line format: \ 'be on the following line format: \neasting '
\neasting northing height pixelrow pixelcol imagename') 'northing height pixelrow pixelcol imagename'))
parser.add_argument('--zip-results', parser.add_argument('--zip-results',
action = 'store_true', action='store_true',
default = False, default=False,
help = 'compress the results using gunzip') help='compress the results using gunzip')
args = parser.parse_args() args = parser.parse_args()
@ -222,51 +240,65 @@ print vars(args)
def run(cmd): def run(cmd):
"""Run a system command"""
returnCode = os.system(cmd) returnCode = os.system(cmd)
if (returnCode != 0): if (returnCode != 0):
sys.exit("\nquitting cause: \n\t" + cmd + "\nreturned with code " + str(returnCode) + ".\n") sys.exit("\nquitting cause: \n\t" + cmd + "\nreturned with code " +
str(returnCode) + ".\n")
def now(): def now():
"""Return the current time"""
return datetime.datetime.now().strftime('%a %b %d %H:%M:%S %Z %Y') return datetime.datetime.now().strftime('%a %b %d %H:%M:%S %Z %Y')
def runAndReturn(cmdSrc, cmdDest):
def run_and_return(cmdSrc, cmdDest):
"""Run a system command and return the output"""
srcProcess = subprocess.Popen(shlex.split(cmdSrc), stdout=subprocess.PIPE) srcProcess = subprocess.Popen(shlex.split(cmdSrc), stdout=subprocess.PIPE)
if cmdDest: if cmdDest:
destProcess = subprocess.Popen(shlex.split(cmdDest), stdin=srcProcess.stdout, stdout=subprocess.PIPE) destProcess = subprocess.Popen(shlex.split(cmdDest),
stdin=srcProcess.stdout,
stdout=subprocess.PIPE)
stdout, stderr = destProcess.communicate() stdout, stderr = destProcess.communicate()
else: else:
stdout, stderr = srcProcess.communicate() stdout, stderr = srcProcess.communicate()
return stdout.decode('ascii') return stdout.decode('ascii')
def calculateEpsg(utmZone, south):
def calculate_EPSG(utmZone, south):
"""Calculate and return the EPSG"""
if south: if south:
return 32700 + utmZone return 32700 + utmZone
else: else:
return 32600 + utmZone return 32600 + utmZone
def parseCoordinateSystem():
if os.path.isfile(jobOptions["jobDir"] + "/odm_georeferencing/coordFile.txt"): def parse_coordinate_system():
with open(jobOptions["jobDir"] + "/odm_georeferencing/coordFile.txt") as f: """Write attributes to jobOptions from coord file"""
if os.path.isfile(jobOptions['jobDir'] +
'/odm_georeferencing/coordFile.txt'):
with open(jobOptions['jobDir'] + '/odm_georeferencing/coordFile.txt') as f:
for lineNumber, line in enumerate(f): for lineNumber, line in enumerate(f):
if lineNumber == 0: if lineNumber == 0:
tokens = line.split(' ') tokens = line.split(' ')
if len(tokens) == 3: if len(tokens) == 3:
utmZoneString = tokens[2][0:len(tokens[2])-2].strip() utmZoneString = tokens[2][0:len(tokens[2])-2].strip()
utmSouthBool = (tokens[2][len(tokens[2])-2].strip() == 'S') utmSouthBool = (tokens[2][len(tokens[2])-2].strip() == 'S')
jobOptions["csString"] = "+datum=WGS84 +proj=utm +zone=" + utmZoneString + (" +south" if utmSouthBool else "") jobOptions['csString'] = '+datum=WGS84 +proj=utm +zone=' + utmZoneString + (' +south' if utmSouthBool else '')
jobOptions["epsg"] = calculateEpsg(int(utmZoneString), utmSouthBool) jobOptions['epsg'] = calculate_EPSG(int(utmZoneString), utmSouthBool)
elif lineNumber == 1: elif lineNumber == 1:
tokens = line.split(' ') tokens = line.split(' ')
if len(tokens) == 2: if len(tokens) == 2:
jobOptions["utmEastOffset"] = int(tokens[0].strip()) jobOptions['utmEastOffset'] = int(tokens[0].strip())
jobOptions["utmNorthOffset"] = int(tokens[1].strip()) jobOptions['utmNorthOffset'] = int(tokens[1].strip())
else: else:
break break
def prepareObjects():
## get the source list def prepare_objects():
source_files = runAndReturn('ls -1', 'egrep "\.[jJ]{1}[pP]{1}[eE]{0,1}[gG]{1}"') """Prepare the jobOptions and fileObjects dicts"""
source_files = run_and_return('ls -1', 'egrep "\.[jJ]{1}[pP]{1}[eE]{0,1}[gG]{1}"')
print "\n - source files - " + now() print "\n - source files - " + now()
@ -274,11 +306,11 @@ def prepareObjects():
filename = filename.rstrip('\n') filename = filename.rstrip('\n')
if not filename: if not filename:
continue continue
file_make = runAndReturn('jhead "' + filename + '"', 'grep "Camera make"') file_make = run_and_return('jhead "' + filename + '"', 'grep "Camera make"')
file_model = runAndReturn('jhead "' + filename + '"', 'grep "Camera model"') file_model = run_and_return('jhead "' + filename + '"', 'grep "Camera model"')
file_focal = runAndReturn('jhead "' + filename + '"', 'grep "Focal length"') file_focal = run_and_return('jhead "' + filename + '"', 'grep "Focal length"')
file_ccd = runAndReturn('jhead "' + filename + '"', 'grep "CCD width"') file_ccd = run_and_return('jhead "' + filename + '"', 'grep "CCD width"')
file_resolution = runAndReturn('jhead "' + filename + '"', 'grep "Resolution"') file_resolution = run_and_return('jhead "' + filename + '"', 'grep "Resolution"')
fileObject = {} fileObject = {}
@ -311,19 +343,19 @@ def prepareObjects():
fileObject["width"] = int(match.group(1).strip()) fileObject["width"] = int(match.group(1).strip())
fileObject["height"] = int(match.group(2).strip()) fileObject["height"] = int(match.group(2).strip())
if not '--force-focal' in args: if '--force-focal' not in args:
match = re.search(":[\ ]*([0-9\.]*)mm", file_focal) match = re.search(":[\ ]*([0-9\.]*)mm", file_focal)
if match: if match:
fileObject["focal"] = float((match.group()[1:-2]).strip()) fileObject["focal"] = float((match.group()[1:-2]).strip())
else: else:
fileObject["focal"] = args.force_focal fileObject["focal"] = args.force_focal
if not '--force-ccd' in args: if '--force-ccd' not in args:
match = re.search(":[\ ]*([0-9\.]*)mm", file_ccd) match = re.search(":[\ ]*([0-9\.]*)mm", file_ccd)
if match: if match:
fileObject["ccd"] = float(match.group()[1:-2].strip()) fileObject["ccd"] = float(match.group()[1:-2].strip())
if (not "ccd" in fileObject) and ("id" in fileObject): if ("ccd" not in fileObject) and ("id" in fileObject):
fileObject["ccd"] = float(ccdWidths[fileObject["id"]]) fileObject["ccd"] = float(ccdWidths[fileObject["id"]])
else: else:
fileObject["ccd"] = args.force_ccd fileObject["ccd"] = args.force_ccd
@ -377,8 +409,7 @@ def prepareObjects():
objects.append(fileObject) objects.append(fileObject)
if "good" not in objectStats:
if not "good" in objectStats:
print "\n found no usable images - quitting\n" print "\n found no usable images - quitting\n"
sys.exit() sys.exit()
else: else:
@ -392,17 +423,17 @@ def prepareObjects():
jobOptions["jobDir"] = jobOptions["srcDir"] + "/reconstruction-with-image-size-" + str(jobOptions["resizeTo"]) jobOptions["jobDir"] = jobOptions["srcDir"] + "/reconstruction-with-image-size-" + str(jobOptions["resizeTo"])
jobOptions["step_1_convert"] = jobOptions["jobDir"] + "/_convert.templist.txt" jobOptions["step_1_convert"] = jobOptions["jobDir"] + "/_convert.templist.txt"
jobOptions["step_1_vlsift"] = jobOptions["jobDir"] + "/_vlsift.templist.txt" jobOptions["step_1_vlsift"] = jobOptions["jobDir"] + "/_vlsift.templist.txt"
jobOptions["step_1_gzip"] = jobOptions["jobDir"] + "/_gzip.templist.txt" jobOptions["step_1_gzip"] = jobOptions["jobDir"] + "/_gzip.templist.txt"
jobOptions["step_2_filelist"] = jobOptions["jobDir"] + "/_filelist.templist.txt" jobOptions["step_2_filelist"] = jobOptions["jobDir"] + "/_filelist.templist.txt"
jobOptions["step_2_macthes_jobs"] = jobOptions["jobDir"] + "/_matches_jobs.templist.txt" jobOptions["step_2_macthes_jobs"] = jobOptions["jobDir"] + "/_matches_jobs.templist.txt"
jobOptions["step_2_matches_dir"] = jobOptions["jobDir"] + "/matches" jobOptions["step_2_matches_dir"] = jobOptions["jobDir"] + "/matches"
jobOptions["step_2_matches"] = jobOptions["jobDir"] + "/matches.init.txt" jobOptions["step_2_matches"] = jobOptions["jobDir"] + "/matches.init.txt"
jobOptions["step_3_filelist"] = jobOptions["jobDir"] + "/list.txt" jobOptions["step_3_filelist"] = jobOptions["jobDir"] + "/list.txt"
jobOptions["step_3_bundlerOptions"] = jobOptions["jobDir"] + "/options.txt" jobOptions["step_3_bundlerOptions"] = jobOptions["jobDir"] + "/options.txt"
try: try:
os.mkdir(jobOptions["jobDir"]) os.mkdir(jobOptions["jobDir"])
@ -411,12 +442,14 @@ def prepareObjects():
for fileObject in objects: for fileObject in objects:
if fileObject["isOk"]: if fileObject["isOk"]:
fileObject["step_0_resizedImage"] = jobOptions["jobDir"] + "/" + fileObject["base"] + ".jpg" fileObject["step_0_resizedImage"] = jobOptions["jobDir"] + "/" + fileObject["base"] + ".jpg"
fileObject["step_1_pgmFile"] = jobOptions["jobDir"] + "/" + fileObject["base"] + ".pgm" fileObject["step_1_pgmFile"] = jobOptions["jobDir"] + "/" + fileObject["base"] + ".pgm"
fileObject["step_1_keyFile"] = jobOptions["jobDir"] + "/" + fileObject["base"] + ".key" fileObject["step_1_keyFile"] = jobOptions["jobDir"] + "/" + fileObject["base"] + ".key"
fileObject["step_1_gzFile"] = jobOptions["jobDir"] + "/" + fileObject["base"] + ".key.gz" fileObject["step_1_gzFile"] = jobOptions["jobDir"] + "/" + fileObject["base"] + ".key.gz"
def resize(): def resize():
"""Resize images"""
print "\n - preparing images - " + now() print "\n - preparing images - " + now()
os.chdir(jobOptions["jobDir"]) os.chdir(jobOptions["jobDir"])
@ -425,15 +458,15 @@ def resize():
if fileObject["isOk"]: if fileObject["isOk"]:
if not os.path.isfile(fileObject["step_0_resizedImage"]): if not os.path.isfile(fileObject["step_0_resizedImage"]):
if jobOptions["resizeTo"] != 0 and ((int(fileObject["width"]) > jobOptions["resizeTo"]) or (fileObject["height"] > jobOptions["resizeTo"])): if jobOptions["resizeTo"] != 0 and ((int(fileObject["width"]) > jobOptions["resizeTo"]) or (fileObject["height"] > jobOptions["resizeTo"])):
sys.stdout.write(" resizing " + fileObject["src"] +" \tto " + fileObject["step_0_resizedImage"]) sys.stdout.write(" resizing " + fileObject["src"] + " \tto " + fileObject["step_0_resizedImage"])
run("convert -resize " + str(jobOptions["resizeTo"]) + "x" + str(jobOptions["resizeTo"]) +" -quality 100 \"" + jobOptions["srcDir"] + "/" + fileObject["src"] + "\" \"" + fileObject["step_0_resizedImage"] + "\"") run("convert -resize " + str(jobOptions["resizeTo"]) + "x" + str(jobOptions["resizeTo"]) + " -quality 100 \"" + jobOptions["srcDir"] + "/" + fileObject["src"] + "\" \"" + fileObject["step_0_resizedImage"] + "\"")
else: else:
sys.stdout.write(" copying " + fileObject["src"] + " \tto " + fileObject["step_0_resizedImage"]) sys.stdout.write(" copying " + fileObject["src"] + " \tto " + fileObject["step_0_resizedImage"])
shutil.copyfile(CURRENT_DIR + "/" + fileObject["src"], fileObject["step_0_resizedImage"]) shutil.copyfile(CURRENT_DIR + "/" + fileObject["src"], fileObject["step_0_resizedImage"])
else: else:
print " using existing " + fileObject["src"] + " \tto " + fileObject["step_0_resizedImage"] print " using existing " + fileObject["src"] + " \tto " + fileObject["step_0_resizedImage"]
file_resolution = runAndReturn('jhead "' + fileObject["step_0_resizedImage"] + '"', 'grep "Resolution"') file_resolution = run_and_return('jhead "' + fileObject["step_0_resizedImage"] + '"', 'grep "Resolution"')
match = re.search(": ([0-9]*) x ([0-9]*)", file_resolution) match = re.search(": ([0-9]*) x ([0-9]*)", file_resolution)
if match: if match:
fileObject["width"] = int(match.group(1).strip()) fileObject["width"] = int(match.group(1).strip())
@ -443,7 +476,9 @@ def resize():
if args.end_with != "resize": if args.end_with != "resize":
getKeypoints() getKeypoints()
def getKeypoints(): def getKeypoints():
"""Run vlsift to create keypoint files for each image"""
print "\n - finding keypoints - " + now() print "\n - finding keypoints - " + now()
os.chdir(jobOptions["jobDir"]) os.chdir(jobOptions["jobDir"])
@ -456,19 +491,19 @@ def getKeypoints():
if fileObject["isOk"]: if fileObject["isOk"]:
if '--lowe-sift' in args: if '--lowe-sift' in args:
vlsiftJobs += "echo -n \"" + str(c) + "/" + str(objectStats["good"]) + " - \" && convert -format pgm \"" + fileObject["step_0_resizedImage"] + "\" \"" + fileObject["step_1_pgmFile"] + "\"" vlsiftJobs += "echo -n \"" + str(c) + "/" + str(objectStats["good"]) + " - \" && convert -format pgm \"" + fileObject["step_0_resizedImage"] + "\" \"" + fileObject["step_1_pgmFile"] + "\""
vlsiftJobs += " && \"" + BIN_PATH + "/sift\" < \"" + fileObject["step_1_pgmFile"] + "\" > \"" + fileObject["step_1_keyFile"] + "\"" vlsiftJobs += " && \"" + BIN_PATH + "/sift\" < \"" + fileObject["step_1_pgmFile"] + "\" > \"" + fileObject["step_1_keyFile"] + "\""
vlsiftJobs += " && gzip -f \"" + fileObject["step_1_keyFile"] + "\"" vlsiftJobs += " && gzip -f \"" + fileObject["step_1_keyFile"] + "\""
vlsiftJobs += " && rm -f \"" + fileObject["step_1_pgmFile"] + "\"" vlsiftJobs += " && rm -f \"" + fileObject["step_1_pgmFile"] + "\""
vlsiftJobs += " && rm -f \"" + fileObject["step_1_keyFile"] + ".sift\"\n" vlsiftJobs += " && rm -f \"" + fileObject["step_1_keyFile"] + ".sift\"\n"
else: else:
if not os.path.isfile(jobOptions["jobDir"] + "/" + fileObject["base"] + ".key.bin"): if not os.path.isfile(jobOptions["jobDir"] + "/" + fileObject["base"] + ".key.bin"):
vlsiftJobs += "echo -n \"" + str(c) + "/" + str(objectStats["good"]) + " - \" && convert -format pgm \"" + fileObject["step_0_resizedImage"] + "\" \"" + fileObject["step_1_pgmFile"] + "\"" vlsiftJobs += "echo -n \"" + str(c) + "/" + str(objectStats["good"]) + " - \" && convert -format pgm \"" + fileObject["step_0_resizedImage"] + "\" \"" + fileObject["step_1_pgmFile"] + "\""
vlsiftJobs += " && \"" + BIN_PATH + "/vlsift\" \"" + fileObject["step_1_pgmFile"] + "\" -o \"" + fileObject["step_1_keyFile"] + ".sift\" > /dev/null && perl \"" + BIN_PATH + "/../convert_vlsift_to_lowesift.pl\" \"" + jobOptions["jobDir"] + "/" + fileObject["base"] + "\"" vlsiftJobs += " && \"" + BIN_PATH + "/vlsift\" \"" + fileObject["step_1_pgmFile"] + "\" -o \"" + fileObject["step_1_keyFile"] + ".sift\" > /dev/null && perl \"" + BIN_PATH + "/../convert_vlsift_to_lowesift.pl\" \"" + jobOptions["jobDir"] + "/" + fileObject["base"] + "\""
vlsiftJobs += " && gzip -f \"" + fileObject["step_1_keyFile"] + "\"" vlsiftJobs += " && gzip -f \"" + fileObject["step_1_keyFile"] + "\""
vlsiftJobs += " && rm -f \"" + fileObject["step_1_pgmFile"] + "\"" vlsiftJobs += " && rm -f \"" + fileObject["step_1_pgmFile"] + "\""
vlsiftJobs += " && rm -f \"" + fileObject["step_1_keyFile"] + ".sift\"\n" vlsiftJobs += " && rm -f \"" + fileObject["step_1_keyFile"] + ".sift\"\n"
else: else:
print "using existing " + jobOptions["jobDir"] + "/" + fileObject["base"] + ".key.bin" print "using existing " + jobOptions["jobDir"] + "/" + fileObject["base"] + ".key.bin"
siftDest = open(jobOptions["step_1_vlsift"], 'w') siftDest = open(jobOptions["step_1_vlsift"], 'w')
@ -480,7 +515,9 @@ def getKeypoints():
if args.end_with != "getKeypoints": if args.end_with != "getKeypoints":
match() match()
def match(): def match():
"""Run matches on images"""
print "\n - matching keypoints - " + now() print "\n - matching keypoints - " + now()
os.chdir(jobOptions["jobDir"]) os.chdir(jobOptions["jobDir"])
@ -491,7 +528,7 @@ def match():
matchesJobs = "" matchesJobs = ""
c = 0 c = 0
t = (objectStats["good"] - 1) * (objectStats["good"] / 2) t = (objectStats["good"] - 1) * (objectStats["good"] / 2) # BUG:unused
preselected_pairs = [] preselected_pairs = []
@ -504,11 +541,11 @@ def match():
matchDest.write(filesList) matchDest.write(filesList)
matchDest.close() matchDest.close()
#Check if preselection is to be run # Check if preselection is to be run
if args.matcher_preselect: if args.matcher_preselect:
useKnn = True useKnn = True
if args.matcher_useKnn: if args.matcher_useKnn:
useKnn = False useKnn = False # BUG: never used
preselected_pairs = knnMatch_exif.preselect_pairs(BIN_PATH + "/odm_extract_utm", jobOptions["step_2_filelist"], args.matcher_kDistance, args.matcher_useKnn) preselected_pairs = knnMatch_exif.preselect_pairs(BIN_PATH + "/odm_extract_utm", jobOptions["step_2_filelist"], args.matcher_kDistance, args.matcher_useKnn)
if len(preselected_pairs) != 0: if len(preselected_pairs) != 0:
for i, j, in preselected_pairs: for i, j, in preselected_pairs:
@ -551,12 +588,14 @@ def match():
matchDest.write(filesList) matchDest.write(filesList)
matchDest.close() matchDest.close()
# run("\"" + BIN_PATH + "/KeyMatchFull\" \"" + jobOptions["step_2_filelist"] + "\" \"" + jobOptions["step_2_matches"] + "\" ") # run("\"" + BIN_PATH + "/KeyMatchFull\" \"" + jobOptions["step_2_filelist"] + "\" \"" + jobOptions["step_2_matches"] + "\" ")
if args.end_with != "match": if args.end_with != "match":
bundler() bundler()
def bundler(): def bundler():
"""Run bundler and prepare bundle for PMVS"""
print "\n - running bundler - " + now() print "\n - running bundler - " + now()
os.chdir(jobOptions["jobDir"]) os.chdir(jobOptions["jobDir"])
@ -590,7 +629,7 @@ def bundler():
filesList = filesList.rstrip('\n') filesList = filesList.rstrip('\n')
bundlerOptions = "--match_table matches.init.txt\n" bundlerOptions = "--match_table matches.init.txt\n"
bundlerOptions += "--output bundle.out\n" bundlerOptions += "--output bundle.out\n"
bundlerOptions += "--output_all bundle_\n" bundlerOptions += "--output_all bundle_\n"
bundlerOptions += "--output_dir bundle\n" bundlerOptions += "--output_dir bundle\n"
@ -626,7 +665,9 @@ def bundler():
if args.end_with != "bundler": if args.end_with != "bundler":
cmvs() cmvs()
def cmvs(): def cmvs():
"""Run CMVS"""
print "\n - running cmvs - " + now() print "\n - running cmvs - " + now()
os.chdir(jobOptions["jobDir"]) os.chdir(jobOptions["jobDir"])
@ -637,7 +678,9 @@ def cmvs():
if args.end_with != "cmvs": if args.end_with != "cmvs":
pmvs() pmvs()
def pmvs(): def pmvs():
"""Run PMVS"""
print "\n - running pmvs - " + now() print "\n - running pmvs - " + now()
os.chdir(jobOptions["jobDir"]) os.chdir(jobOptions["jobDir"])
@ -649,7 +692,9 @@ def pmvs():
if args.end_with != "pmvs": if args.end_with != "pmvs":
odm_meshing() odm_meshing()
def odm_meshing(): def odm_meshing():
"""Run odm_meshing"""
print "\n - running meshing - " + now() print "\n - running meshing - " + now()
os.chdir(jobOptions["jobDir"]) os.chdir(jobOptions["jobDir"])
@ -663,7 +708,9 @@ def odm_meshing():
if args.end_with != "odm_meshing": if args.end_with != "odm_meshing":
odm_texturing() odm_texturing()
def odm_texturing(): def odm_texturing():
"""Run odm_texturing"""
print "\n - running texturing - " + now() print "\n - running texturing - " + now()
os.chdir(jobOptions["jobDir"]) os.chdir(jobOptions["jobDir"])
@ -673,12 +720,14 @@ def odm_texturing():
except: except:
pass pass
run("\"" + BIN_PATH + "/odm_texturing\" -bundleFile " + jobOptions["jobDir"] + "/pmvs/bundle.rd.out -imagesPath "+ jobOptions["srcDir"] + "/ -imagesListPath " + jobOptions["jobDir"] + "/pmvs/list.rd.txt -inputModelPath " + jobOptions["jobDir"] + "-results/odm_mesh-0000.ply -outputFolder " + jobOptions["jobDir"] + "-results/odm_texturing/ -textureResolution " + str(args.odm_texturing_textureResolution) + " -bundleResizedTo " + str(jobOptions["resizeTo"]) + " -textureWithSize " + str(args.odm_texturing_textureWithSize) + " -logFile " + jobOptions["jobDir"] + "/odm_texturing/odm_texturing_log.txt") run("\"" + BIN_PATH + "/odm_texturing\" -bundleFile " + jobOptions["jobDir"] + "/pmvs/bundle.rd.out -imagesPath " + jobOptions["srcDir"] + "/ -imagesListPath " + jobOptions["jobDir"] + "/pmvs/list.rd.txt -inputModelPath " + jobOptions["jobDir"] + "-results/odm_mesh-0000.ply -outputFolder " + jobOptions["jobDir"] + "-results/odm_texturing/ -textureResolution " + str(args.odm_texturing_textureResolution) + " -bundleResizedTo " + str(jobOptions["resizeTo"]) + " -textureWithSize " + str(args.odm_texturing_textureWithSize) + " -logFile " + jobOptions["jobDir"] + "/odm_texturing/odm_texturing_log.txt")
if args.end_with != "odm_texturing": if args.end_with != "odm_texturing":
odm_georeferencing() odm_georeferencing()
def odm_georeferencing(): def odm_georeferencing():
"""Run odm_georeferencing"""
print "\n - running georeferencing - " + now() print "\n - running georeferencing - " + now()
os.chdir(jobOptions["jobDir"]) os.chdir(jobOptions["jobDir"])
@ -687,7 +736,7 @@ def odm_georeferencing():
except: except:
pass pass
if args.odm_georeferencing_useGcp == False: if not args.odm_georeferencing_useGcp:
run("\"" + BIN_PATH + "/odm_extract_utm\" -imagesPath " + jobOptions["srcDir"] + "/ -imageListFile " + jobOptions["jobDir"] + "/pmvs/list.rd.txt -outputCoordFile " + jobOptions["jobDir"] + "/odm_georeferencing/coordFile.txt") run("\"" + BIN_PATH + "/odm_extract_utm\" -imagesPath " + jobOptions["srcDir"] + "/ -imageListFile " + jobOptions["jobDir"] + "/pmvs/list.rd.txt -outputCoordFile " + jobOptions["jobDir"] + "/odm_georeferencing/coordFile.txt")
run("\"" + BIN_PATH + "/odm_georef\" -bundleFile " + jobOptions["jobDir"] + "/pmvs/bundle.rd.out -inputCoordFile " + jobOptions["jobDir"] + "/odm_georeferencing/coordFile.txt -inputFile " + jobOptions["jobDir"] + "-results/odm_texturing/odm_textured_model.obj -outputFile " + jobOptions["jobDir"] + "-results/odm_texturing/odm_textured_model_geo.obj -inputPointCloudFile " + jobOptions["jobDir"] + "-results/option-0000.ply -outputPointCloudFile " + jobOptions["jobDir"] + "-results/option-0000_georef.ply -logFile " + jobOptions["jobDir"] + "/odm_georeferencing/odm_georeferencing_log.txt -georefFileOutputPath " + jobOptions["jobDir"] + "-results/odm_texturing/odm_textured_model_geo_georef_system.txt") run("\"" + BIN_PATH + "/odm_georef\" -bundleFile " + jobOptions["jobDir"] + "/pmvs/bundle.rd.out -inputCoordFile " + jobOptions["jobDir"] + "/odm_georeferencing/coordFile.txt -inputFile " + jobOptions["jobDir"] + "-results/odm_texturing/odm_textured_model.obj -outputFile " + jobOptions["jobDir"] + "-results/odm_texturing/odm_textured_model_geo.obj -inputPointCloudFile " + jobOptions["jobDir"] + "-results/option-0000.ply -outputPointCloudFile " + jobOptions["jobDir"] + "-results/option-0000_georef.ply -logFile " + jobOptions["jobDir"] + "/odm_georeferencing/odm_georeferencing_log.txt -georefFileOutputPath " + jobOptions["jobDir"] + "-results/odm_texturing/odm_textured_model_geo_georef_system.txt")
elif os.path.isfile(jobOptions["srcDir"] + "/" + args.odm_georeferencing_gcpFile): elif os.path.isfile(jobOptions["srcDir"] + "/" + args.odm_georeferencing_gcpFile):
@ -697,8 +746,8 @@ def odm_georeferencing():
print "Skipping orthophoto" print "Skipping orthophoto"
args.end_with = "odm_georeferencing" args.end_with = "odm_georeferencing"
if not "csString" in jobOptions: if "csString" not in jobOptions:
parseCoordinateSystem() parse_coordinate_system()
if "csString" in jobOptions and "utmEastOffset" in jobOptions and "utmNorthOffset" in jobOptions: if "csString" in jobOptions and "utmEastOffset" in jobOptions and "utmNorthOffset" in jobOptions:
images = [] images = []
@ -726,7 +775,7 @@ def odm_georeferencing():
latString = tokens[1] # Example: 41d2'11.789"N latString = tokens[1] # Example: 41d2'11.789"N
altString = "" altString = ""
if len(tokens) > 2: if len(tokens) > 2:
altString = tokens[2] # Example: 0.998 altString = tokens[2] # Example: 0.998
tokens = re.split("[d '\"]+", lonString) tokens = re.split("[d '\"]+", lonString)
if len(tokens) >= 4: if len(tokens) >= 4:
@ -754,7 +803,7 @@ def odm_georeferencing():
exivCmd += " -M\"set Exif.GPSInfo.GPSLongitude " + lonDeg + "/1 " + lonMin + "/1 " + lonSecNumerator + "/" + lonSecDenominator + "\"" exivCmd += " -M\"set Exif.GPSInfo.GPSLongitude " + lonDeg + "/1 " + lonMin + "/1 " + lonSecNumerator + "/" + lonSecDenominator + "\""
exivCmd += " -M\"set Exif.GPSInfo.GPSLongitudeRef " + lonRef + "\"" exivCmd += " -M\"set Exif.GPSInfo.GPSLongitudeRef " + lonRef + "\""
altNumerator = arcDenominator = 0 altNumerator = arcDenominator = 0 # BUG: arcDenominator is never used
if altString: if altString:
altFrac = fractions.Fraction(altString) altFrac = fractions.Fraction(altString)
altNumerator = str(altFrac._numerator) altNumerator = str(altFrac._numerator)
@ -779,6 +828,7 @@ def odm_georeferencing():
def odm_orthophoto(): def odm_orthophoto():
"""Run odm_orthophoto"""
print "\n - running orthophoto generation - " + now() print "\n - running orthophoto generation - " + now()
os.chdir(jobOptions["jobDir"]) os.chdir(jobOptions["jobDir"])
@ -789,58 +839,69 @@ def odm_orthophoto():
run("\"" + BIN_PATH + "/odm_orthophoto\" -inputFile " + jobOptions["jobDir"] + "-results/odm_texturing/odm_textured_model_geo.obj -logFile " + jobOptions["jobDir"] + "/odm_orthophoto/odm_orthophoto_log.txt -outputFile " + jobOptions["jobDir"] + "-results/odm_orthphoto.png -resolution 20.0 -outputCornerFile " + jobOptions["jobDir"] + "/odm_orthphoto_corners.txt") run("\"" + BIN_PATH + "/odm_orthophoto\" -inputFile " + jobOptions["jobDir"] + "-results/odm_texturing/odm_textured_model_geo.obj -logFile " + jobOptions["jobDir"] + "/odm_orthophoto/odm_orthophoto_log.txt -outputFile " + jobOptions["jobDir"] + "-results/odm_orthphoto.png -resolution 20.0 -outputCornerFile " + jobOptions["jobDir"] + "/odm_orthphoto_corners.txt")
if not "csString" in jobOptions: if "csString" not in jobOptions:
parseCoordinateSystem() parse_coordinate_system()
geoTiffCreated = False geoTiffCreated = False
if "csString" in jobOptions and "utmEastOffset" in jobOptions and "utmNorthOffset" in jobOptions: if ("csString" in jobOptions and
"utmEastOffset" in jobOptions and "utmNorthOffset" in jobOptions):
ulx = uly = lrx = lry = 0.0 ulx = uly = lrx = lry = 0.0
with open(jobOptions["jobDir"] + "/odm_orthphoto_corners.txt") as f: with open(jobOptions["jobDir"] +
"/odm_orthphoto_corners.txt") as f:
for lineNumber, line in enumerate(f): for lineNumber, line in enumerate(f):
if lineNumber == 0: if lineNumber == 0:
tokens = line.split(' ') tokens = line.split(' ')
if len(tokens) == 4: if len(tokens) == 4:
ulx = float(tokens[0]) + float(jobOptions["utmEastOffset"]) ulx = float(tokens[0]) + \
lry = float(tokens[1]) + float(jobOptions["utmNorthOffset"]) float(jobOptions["utmEastOffset"])
lrx = float(tokens[2]) + float(jobOptions["utmEastOffset"]) lry = float(tokens[1]) + \
uly = float(tokens[3]) + float(jobOptions["utmNorthOffset"]) float(jobOptions["utmNorthOffset"])
lrx = float(tokens[2]) + \
float(jobOptions["utmEastOffset"])
uly = float(tokens[3]) + \
float(jobOptions["utmNorthOffset"])
print(" Creating GeoTIFF...") print(" Creating GeoTIFF...")
sys.stdout.write(" ") sys.stdout.write(" ")
run("gdal_translate -a_ullr " + str(ulx) + " " + str(uly) + " " + str(lrx) + " " + str(lry) + " -a_srs \"" + jobOptions["csString"] + "\" " + jobOptions["jobDir"] + "-results/odm_orthphoto.png " + jobOptions["jobDir"] + "-results/odm_orthphoto.tif") run("gdal_translate -a_ullr " + str(ulx) + " " + str(uly) + " " +
str(lrx) + " " + str(lry) + " -a_srs \"" + jobOptions["csString"] +
"\" " + jobOptions["jobDir"] + "-results/odm_orthphoto.png " +
jobOptions["jobDir"] + "-results/odm_orthphoto.tif")
geoTiffCreated = True geoTiffCreated = True
if not geoTiffCreated: if not geoTiffCreated:
print " Warning: No geo-referenced orthophoto created due to missing geo-referencing or corner coordinates." print(" Warning: No geo-referenced orthophoto created due to "
"missing geo-referencing or corner coordinates.")
#parseArgs() if __name__ == '__main__':
prepareObjects() prepare_objects()
os.chdir(jobOptions["jobDir"]) os.chdir(jobOptions["jobDir"])
if args.start_with == "resize": if args.start_with == "resize":
resize() resize()
elif args.start_with == "getKeypoints": elif args.start_with == "getKeypoints":
getKeypoints() getKeypoints()
elif args.start_with == "match": elif args.start_with == "match":
match() match()
elif args.start_with == "bundler": elif args.start_with == "bundler":
bundler() bundler()
elif args.start_with == "cmvs": elif args.start_with == "cmvs":
cmvs() cmvs()
elif args.start_with == "pmvs": elif args.start_with == "pmvs":
pmvs() pmvs()
elif args.start_with == "odm_meshing": elif args.start_with == "odm_meshing":
odm_meshing() odm_meshing()
elif args.start_with == "odm_texturing": elif args.start_with == "odm_texturing":
odm_texturing() odm_texturing()
elif args.start_with == "odm_georeferencing": elif args.start_with == "odm_georeferencing":
odm_georeferencing() odm_georeferencing()
elif args.start_with == "odm_orthophoto": elif args.start_with == "odm_orthophoto":
odm_orthophoto() odm_orthophoto()
if args.zip_results: if args.zip_results:
print "\nCompressing results - " + now() print "\nCompressing results - " + now()
run("cd " + jobOptions["jobDir"] + "-results/ && tar -czf " + jobOptions["jobDir"] + "-results.tar.gz *") run("cd " + jobOptions["jobDir"] + "-results/ && tar -czf " +
jobOptions["jobDir"] + "-results.tar.gz *")
print "\n - done - " + now() print "\n - done - " + now()