def main(argsIn):

    try:
        # Sample usage:
        # python full_processing_script.py \
        #  --yyyymmdd 20091016 --site AN --num-processes 1 --num-threads 12 --bundle-length 12 \
        #  --start-frame 350 --stop-frame 353 --skip-validate \
        # --camera-calibration-folder camera_calib  \
        # --reference-dem-folder ref_dem_folder
        # An output folder will be crated automatically (with a name like
        # AN_20091016), or its name can be specified via the --output-folder
        # option.
        usage = '''full_processing_script.py <options>'''
                      
        parser = argparse.ArgumentParser(usage=usage)

        # Run selection
        parser.add_argument("--yyyymmdd",  dest="yyyymmdd", required=True,
                          help="Specify the year, month, and day in one YYYYMMDD string.")
        parser.add_argument("--site",  dest="site", required=True,
                          help="Name of the location of the images (AN, GR, or AL)")

        parser.add_argument("--output-folder",  dest="outputFolder", default=None,
                          help="Name of the output folder. If not specified, " + \
                          "use something like AN_YYYYMMDD.")

        parser.add_argument("--camera-lookup-file",  dest="cameraLookupFile", default=None,
                          help="The file to use to find which camera was used for which "  + \
                          "flight. By default it is in the same directory as this script " + \
                          "and named camera_lookup.txt.")
        
        # Processing options
        parser.add_argument('--bundle-length', dest='bundleLength', default=2,
                          type=int, help="The number of images to bundle adjust and process " + \
                          "in a single batch.")
        # TODO: Compute this automatically??
        parser.add_argument('--overlap-limit', dest='overlapLimit', default=2,
                          type=int, help="The number of images to treat as overlapping for " + \
                          "bundle adjustment.")
        
        parser.add_argument('--stereo-arguments', dest='stereoArgs',
                            # set --min-xcorr-level 0 to do the left-to-right 
                            # and right-to-left consistency check at the lowest level.
                            default='--stereo-algorithm 2 --min-xcorr-level 0',
                            help='Extra arguments to pass to stereo.')

        parser.add_argument('--start-frame', dest='startFrame', type=int,
                          default=icebridge_common.getSmallestFrame(),
                          help="Frame to start with.  Leave this and stop-frame blank to " + \
                          "process all frames.")
        parser.add_argument('--stop-frame', dest='stopFrame', type=int,
                          default=icebridge_common.getLargestFrame(),
                          help='Frame to stop on.')
        parser.add_argument('--frames-file', dest='framesFile', default="",
                            help='Specific frames to run ortho2pinhole on within this frame range.')

        parser.add_argument('--max-num-lidar-to-fetch', dest='maxNumLidarToFetch', default=None,
                          type=int, help="The maximum number of lidar files to fetch. " + \
                          "This is used in debugging.")
        
        parser.add_argument("--camera-calibration-folder",  dest="inputCalFolder", default=None,
                          help="The folder containing camera calibration.")

        parser.add_argument("--input-calibration-camera",  dest="inputCalCamera", default="",
                            help="Instead of looking up the calibrated camera in the calibration folder, use this one.")
        
        parser.add_argument("--output-calibration-camera",  dest="outputCalCamera", default="",
                            help="If specified, float the intrinsics and write the optimized model here.")

        parser.add_argument("--output-model-type",  dest="outputModelType", default="RPC",
                            help="Generate a distortion model of type RPC, RPC5, or RPC6.")
        parser.add_argument("--reference-dem-folder",  dest="refDemFolder", default=None,
                          help="The folder containing DEMs that created orthoimages.")

        parser.add_argument("--processing-subfolder",  dest="processingSubfolder", default=None,
                          help="Specify a subfolder name where the processing outputs will go. " + \
                          "fault is no additional folder")
                          
        parser.add_argument("--simple-cameras", action="store_true", dest="simpleCameras", default=False,
                          help="Don't use orthoimages to refine the camera models.")

        # This option is only needed when generating camera models from the nav files.
        parser.add_argument('--camera-mounting', default=0, dest='cameraMounting', type=int,
              help='0=right-forwards, 1=left-forwards, 2=top-forwards, 3=bottom-forwards.')

        # Performance options  
        parser.add_argument('--num-processes', dest='numProcesses', default=1,
                          type=int, help='The number of simultaneous processes to run.')
        parser.add_argument('--num-ortho-processes', dest='numOrthoProcesses', default=-1,
                          type=int, help='The number of simultaneous ortho processes to run.')
        parser.add_argument('--num-threads', dest='numThreads', default=8,
                          type=int, help='The number of threads per process.')

        # Action control
        parser.add_argument("--skip-fetch", action="store_true", dest="noFetch", default=False,
                          help="Skip data fetching.")
        parser.add_argument("--skip-convert", action="store_true", dest="noConvert", default=False,
                          help="Skip data conversion.")
        parser.add_argument("--stop-after-fetch", action="store_true", dest="stopAfterFetch",
                          default=False,
                          help="Stop program after data fetching.")
        parser.add_argument("--stop-after-convert", action="store_true", dest="stopAfterConvert",
                          default=False,
                          help="Stop program after data conversion.")
        parser.add_argument("--skip-validate", action="store_true", dest="skipValidate",
                            default=False,
                            help="Skip input data validation.")
        parser.add_argument("--ignore-missing-lidar", action="store_true", dest="ignoreMissingLidar",
                            default=False,
                            help="Keep going if the lidar is missing.")
        parser.add_argument("--log-batches", action="store_true", dest="logBatches", default=False,
                          help="Log the required batch commands without running them.")
        parser.add_argument('--cleanup', action='store_true', default=False, dest='cleanup',  
                          help='If the final result is produced delete intermediate files.')
        parser.add_argument('--many-ip', action='store_true', default=False, dest='manyip',  
                          help='If to use a lot of IP in bundle adjustment from the beginning.')
        parser.add_argument("--dry-run", action="store_true", dest="dryRun", default=False,
                          help="Set up the input directories but do not fetch/process any imagery.")

        parser.add_argument("--refetch", action="store_true", dest="reFetch", default=False,
                          help="Try fetching again if some files turned out invalid " + \
                          "during conversions.")
        parser.add_argument("--refetch-index", action="store_true", dest="refetchIndex",
                          default=False,
                          help="Force refetch of the index file.")
        parser.add_argument("--refetch-nav", action="store_true", dest="refetchNav",
                          default=False,
                          help="Force refetch of the nav file.")
        parser.add_argument("--stop-after-index-fetch", action="store_true",
                          dest="stopAfterIndexFetch", default=False,
                          help="Stop after fetching the indices.")

        parser.add_argument("--no-nav", action="store_true", dest="noNavFetch",
                            default=False, help="Don't fetch or convert the nav data.")
                       
        parser.add_argument("--no-lidar-convert", action="store_true", dest="noLidarConvert",
                          default=False,
                          help="Skip lidar files in the conversion step.")
        parser.add_argument("--no-ortho-convert", action="store_true", dest="noOrthoConvert",
                          default=False,
                          help="Skip generating camera models in the conversion step.")
        parser.add_argument("--skip-fast-conversions", action="store_true", dest="skipFastConvert",
                          default=False,
                          help="Skips all non-ortho conversions.")
                          
        options = parser.parse_args(argsIn)

    except argparse.ArgumentError as msg:
        parser.error(msg)

    icebridge_common.switchWorkDir()
    
    if options.numOrthoProcesses < 0:
        options.numOrthoProcesses = options.numProcesses
        
    isSouth = icebridge_common.checkSite(options.site)

    # Turned off elevation limits here since they are being set from LIDAR data.
    ## Add the site based elevation limits to the stereoArgs option
    #altLimits = icebridge_common.getElevationLimits(options.site)
    #options.stereoArgs = (' %s --elevation-limit %f %f ' 
    #                      % (options.stereoArgs, altLimits[0], altLimits[1]))
    options.stereoArgs = (' %s ' % (options.stereoArgs))

    if options.cameraLookupFile is None:
        options.cameraLookupFile = P.join(basepath, 'camera_lookup.txt')
    if not os.path.isfile(options.cameraLookupFile):
        raise Exception("Can't find camera file: " + options.cameraLookupFile)
        
    if len(options.yyyymmdd) != 8 and len(options.yyyymmdd) != 9:
        # Make an exception for 20100422a
        raise Exception("The --yyyymmdd field must have length 8 or 9.")

    if options.outputFolder is None:
        options.outputFolder = icebridge_common.outputFolder(options.site, options.yyyymmdd)

    if options.stopAfterIndexFetch:
        options.stopAfterFetch = True
        
    os.system('mkdir -p ' + options.outputFolder)
    logLevel = logging.INFO # Record everything
    logger   = icebridge_common.setUpLogger(options.outputFolder, logLevel,
                                            'icebridge_processing_log_frames_' + \
                                            str(options.startFrame) + "_" + str(options.stopFrame))

    # Make sure we later know what we were doing
    logger.info("full_processing_script.py " + " ".join(argsIn)) 
                
    (out, err, status) = asp_system_utils.executeCommand(['uname', '-a'],
                                                         suppressOutput = True)
    logger.info("Running on machine: " + out)
    logger.info("Work dir is " + os.getcwd())

    os.system("ulimit -c 0") # disable core dumps
    os.system("umask 022")   # enforce files be readable by others
    
    # Perform some input checks and initializations
    # These are not needed unless cameras are initialized 
    if options.inputCalFolder is None or not os.path.exists(options.inputCalFolder):
        raise Exception("Missing camera calibration folder.")
    if options.refDemFolder is None or not os.path.exists(options.refDemFolder):
        raise Exception("Missing reference DEM folder.")
    
    refDemName = icebridge_common.getReferenceDemName(options.site)
    refDemPath = os.path.join(options.refDemFolder, refDemName)
    if not os.path.exists(refDemPath):
        raise Exception("Missing reference DEM: " + refDemPath)
    
    # TODO: CLEAN UP!!!
    # Set up the output folders
    cameraFolder       = icebridge_common.getCameraFolder(options.outputFolder)
    imageFolder        = icebridge_common.getImageFolder(options.outputFolder)
    jpegFolder         = icebridge_common.getJpegFolder(options.outputFolder)
    orthoFolder        = icebridge_common.getOrthoFolder(options.outputFolder)
    fireballFolder     = icebridge_common.getFireballFolder(options.outputFolder)
    corrFireballFolder = icebridge_common.getCorrFireballFolder(options.outputFolder)
    lidarFolder        = icebridge_common.getLidarFolder(options.outputFolder)
    navFolder          = icebridge_common.getNavFolder(options.outputFolder)
    navCameraFolder    = icebridge_common.getNavCameraFolder(options.outputFolder)
    processedFolder    = icebridge_common.getProcessedFolder(options.outputFolder)

    if options.outputCalCamera != "":
        # Prepare to solve for intrinsics. Note that this modifies some things along the way.
        (options, cameraFolder, navCameraFolder, processedFolder) = \
                  solveIntrinsics_Part1(options, jpegFolder, cameraFolder, navCameraFolder,
                                        processedFolder, logger)
        
    # Handle subfolder option.  This is useful for comparing results with different parameters!
    if options.processingSubfolder:
        processedFolder = os.path.join(processedFolder, options.processingSubfolder)
        logger.info('Will write to processing subfolder: ' + options.processingSubfolder)
       
    # If something failed in the first attempt either in fetch or in
    # convert, we will wipe bad files, and try to refetch/re-convert.
    numAttempts = 1
    if options.reFetch and (not options.noFetch):
        numAttempts = 2
    
    for attempt in range(numAttempts):
        if numAttempts > 1:
            logger.info("Fetch/convert attempt: " + str(attempt+1))
        ans = runFetchConvert(options, isSouth, cameraFolder, imageFolder, jpegFolder, orthoFolder,
                              fireballFolder, corrFireballFolder, lidarFolder, processedFolder,
                              navFolder, navCameraFolder, refDemPath, logger)
        if ans == 0:
            break
        
    if options.stopAfterFetch or options.dryRun or options.stopAfterConvert:
        logger.info('Fetch/convert finished!')
        return 0

       
    # Call the processing routine
    processTheRun(options, imageFolder, cameraFolder, lidarFolder, orthoFolder,
                  corrFireballFolder, processedFolder,
                  isSouth, refDemPath)
   
    if options.outputCalCamera != "":
        # Finish solving for intrinscs. 
        solveIntrinsics_Part2(options, imageFolder, cameraFolder, lidarFolder, orthoFolder,
                              processedFolder, isSouth, logger)
def validateOrthosAndFireball(options, fileType, logger):
    '''Validate ortho and fireball files within the current frame range. This
    is expected to be in called in parallel for smaller chunks. Lidar files
    will be validated serially. Jpegs get validated when converted to tif.
    Return True if all is good.'''

    badFiles = False
    logger.info("Validating files of type: " + fileType)
    
    if fileType   == 'ortho':
        dataFolder = icebridge_common.getOrthoFolder(options.outputFolder)
    elif fileType == 'fireball':
        dataFolder = icebridge_common.getFireballFolder(options.outputFolder)
    else:
        raise Exception("Unknown file type: " + fileType)

    indexPath = icebridge_common.csvIndexFile(dataFolder)
    if not os.path.exists(indexPath):
        # The issue of what to do when the index does not exist should
        # have been settled by now.
        return (not badFiles)

    # Fetch from disk the set of already validated files, if any
    validFilesList = icebridge_common.validFilesList(options.outputFolder,
                                                     options.startFrame, options.stopFrame)
    validFilesSet = set()
    validFilesSet = icebridge_common.updateValidFilesListFromDisk(validFilesList, validFilesSet)
    numInitialValidFiles = len(validFilesSet)
    
    (frameDict, urlDict) = icebridge_common.readIndexFile(indexPath, prependFolder = True)
    for frame in frameDict.keys():

        if frame < options.startFrame or frame > options.stopFrame:
            continue

        outputPath = frameDict[frame]
        xmlFile = icebridge_common.xmlFile(outputPath)

        if outputPath in validFilesSet and os.path.exists(outputPath) and \
            xmlFile in validFilesSet and os.path.exists(xmlFile):
            #logger.info('Previously validated: ' + outputPath + ' ' + xmlFile)
            continue
        else:
            isGood = icebridge_common.hasValidChkSum(outputPath, logger)
            if not isGood:
                logger.info('Found invalid data. Will wipe: ' + outputPath + ' ' + xmlFile)
                os.system('rm -f ' + outputPath) # will not throw
                os.system('rm -f ' + xmlFile) # will not throw
                badFiles = True
            else:
                logger.info('Valid file: ' + outputPath)
                validFilesSet.add(outputPath)
                validFilesSet.add(xmlFile)
            
        if fileType != 'fireball':
            continue

        # Also validate tfw
        tfwFile = icebridge_common.tfwFile(outputPath)
        xmlFile = icebridge_common.xmlFile(tfwFile)
        if tfwFile in validFilesSet and os.path.exists(tfwFile) and \
            xmlFile in validFilesSet and os.path.exists(xmlFile):
            #logger.info('Previously validated: ' + tfwFile + ' ' + xmlFile)
            continue
        else:
            isGood = icebridge_common.isValidTfw(tfwFile, logger)
            if not isGood:
                logger.info('Found invalid tfw. Will wipe: ' + tfwFile + ' ' + xmlFile)
                os.system('rm -f ' + tfwFile) # will not throw
                os.system('rm -f ' + xmlFile) # will not throw
                badFiles = True
            else:
                logger.info('Valid tfw file: ' + tfwFile)
                validFilesSet.add(tfwFile)
                validFilesSet.add(xmlFile)
        
    # Write to disk the list of validated files, but only if new
    # validations happened.  First re-read that list, in case a
    # different process modified it in the meantime, such as if two
    # managers are running at the same time.
    numFinalValidFiles = len(validFilesSet)
    if numInitialValidFiles != numFinalValidFiles:
        validFilesSet = \
                      icebridge_common.updateValidFilesListFromDisk(validFilesList, validFilesSet)
        icebridge_common.writeValidFilesList(validFilesList, validFilesSet)

    return (not badFiles)
    if options.refDemFolder is None or not os.path.exists(
            options.refDemFolder):
        raise Exception("Missing reference DEM folder.")

    refDemName = icebridge_common.getReferenceDemName(options.site)
    refDemPath = os.path.join(options.refDemFolder, refDemName)
    if not os.path.exists(refDemPath):
        raise Exception("Missing reference DEM: " + refDemPath)

    # TODO: CLEAN UP!!!
    # Set up the output folders
    cameraFolder = icebridge_common.getCameraFolder(options.outputFolder)
    imageFolder = icebridge_common.getImageFolder(options.outputFolder)
    jpegFolder = icebridge_common.getJpegFolder(options.outputFolder)
    orthoFolder = icebridge_common.getOrthoFolder(options.outputFolder)
    fireballFolder = icebridge_common.getFireballFolder(options.outputFolder)
    corrFireballFolder = icebridge_common.getCorrFireballFolder(
        options.outputFolder)
    lidarFolder = icebridge_common.getLidarFolder(options.outputFolder)
    navFolder = icebridge_common.getNavFolder(options.outputFolder)
    navCameraFolder = icebridge_common.getNavCameraFolder(options.outputFolder)
    processedFolder = icebridge_common.getProcessedFolder(options.outputFolder)

    if options.outputCalCamera != "":
        # Prepare to solve for intrinsics. Note that this modifies some things along the way.
        (options, cameraFolder, navCameraFolder, processedFolder) = \
                  solveIntrinsics_Part1(options, jpegFolder, cameraFolder, navCameraFolder,
                                        processedFolder, logger)

    # Handle subfolder option.  This is useful for comparing results with different parameters!
    if options.processingSubfolder:
def validateOrthosAndFireball(options, fileType, logger):
    '''Validate ortho and fireball files within the current frame range. This
    is expected to be in called in parallel for smaller chunks. Lidar files
    will be validated serially. Jpegs get validated when converted to tif.
    Return True if all is good.'''

    badFiles = False
    logger.info("Validating files of type: " + fileType)

    if fileType == 'ortho':
        dataFolder = icebridge_common.getOrthoFolder(options.outputFolder)
    elif fileType == 'fireball':
        dataFolder = icebridge_common.getFireballFolder(options.outputFolder)
    else:
        raise Exception("Unknown file type: " + fileType)

    indexPath = icebridge_common.csvIndexFile(dataFolder)
    if not os.path.exists(indexPath):
        # The issue of what to do when the index does not exist should
        # have been settled by now.
        return (not badFiles)

    # Fetch from disk the set of already validated files, if any
    validFilesList = icebridge_common.validFilesList(options.outputFolder,
                                                     options.startFrame,
                                                     options.stopFrame)
    validFilesSet = set()
    validFilesSet = icebridge_common.updateValidFilesListFromDisk(
        validFilesList, validFilesSet)
    numInitialValidFiles = len(validFilesSet)

    (frameDict, urlDict) = icebridge_common.readIndexFile(indexPath,
                                                          prependFolder=True)
    for frame in frameDict.keys():

        if frame < options.startFrame or frame > options.stopFrame:
            continue

        outputPath = frameDict[frame]
        xmlFile = icebridge_common.xmlFile(outputPath)

        if outputPath in validFilesSet and os.path.exists(outputPath) and \
            xmlFile in validFilesSet and os.path.exists(xmlFile):
            #logger.info('Previously validated: ' + outputPath + ' ' + xmlFile)
            continue
        else:
            isGood = icebridge_common.hasValidChkSum(outputPath, logger)
            if not isGood:
                logger.info('Found invalid data. Will wipe: ' + outputPath +
                            ' ' + xmlFile)
                os.system('rm -f ' + outputPath)  # will not throw
                os.system('rm -f ' + xmlFile)  # will not throw
                badFiles = True
            else:
                logger.info('Valid file: ' + outputPath)
                validFilesSet.add(outputPath)
                validFilesSet.add(xmlFile)

        if fileType != 'fireball':
            continue

        # Also validate tfw
        tfwFile = icebridge_common.tfwFile(outputPath)
        xmlFile = icebridge_common.xmlFile(tfwFile)
        if tfwFile in validFilesSet and os.path.exists(tfwFile) and \
            xmlFile in validFilesSet and os.path.exists(xmlFile):
            #logger.info('Previously validated: ' + tfwFile + ' ' + xmlFile)
            continue
        else:
            isGood = icebridge_common.isValidTfw(tfwFile, logger)
            if not isGood:
                logger.info('Found invalid tfw. Will wipe: ' + tfwFile + ' ' +
                            xmlFile)
                os.system('rm -f ' + tfwFile)  # will not throw
                os.system('rm -f ' + xmlFile)  # will not throw
                badFiles = True
            else:
                logger.info('Valid tfw file: ' + tfwFile)
                validFilesSet.add(tfwFile)
                validFilesSet.add(xmlFile)

    # Write to disk the list of validated files, but only if new
    # validations happened.  First re-read that list, in case a
    # different process modified it in the meantime, such as if two
    # managers are running at the same time.
    numFinalValidFiles = len(validFilesSet)
    if numInitialValidFiles != numFinalValidFiles:
        validFilesSet = \
                      icebridge_common.updateValidFilesListFromDisk(validFilesList, validFilesSet)
        icebridge_common.writeValidFilesList(validFilesList, validFilesSet)

    return (not badFiles)
Beispiel #5
0
def main(argsIn):

    try:
        # Sample usage:
        # python full_processing_script.py \
        #  --yyyymmdd 20091016 --site AN --num-processes 1 --num-threads 12 --bundle-length 12 \
        #  --start-frame 350 --stop-frame 353 --skip-validate \
        # --camera-calibration-folder camera_calib  \
        # --reference-dem-folder ref_dem_folder
        # An output folder will be crated automatically (with a name like
        # AN_20091016), or its name can be specified via the --output-folder
        # option.
        usage = '''full_processing_script.py <options>'''

        parser = argparse.ArgumentParser(usage=usage)

        # Run selection
        parser.add_argument(
            "--yyyymmdd",
            dest="yyyymmdd",
            required=True,
            help="Specify the year, month, and day in one YYYYMMDD string.")
        parser.add_argument(
            "--site",
            dest="site",
            required=True,
            help="Name of the location of the images (AN, GR, or AL)")

        parser.add_argument("--output-folder",  dest="outputFolder", default=None,
                          help="Name of the output folder. If not specified, " + \
                          "use something like AN_YYYYMMDD.")

        parser.add_argument("--camera-lookup-file",  dest="cameraLookupFile", default=None,
                          help="The file to use to find which camera was used for which "  + \
                          "flight. By default it is in the same directory as this script " + \
                          "and named camera_lookup.txt.")

        # Processing options
        parser.add_argument('--bundle-length', dest='bundleLength', default=2,
                          type=int, help="The number of images to bundle adjust and process " + \
                          "in a single batch.")
        # TODO: Compute this automatically??
        parser.add_argument('--overlap-limit', dest='overlapLimit', default=2,
                          type=int, help="The number of images to treat as overlapping for " + \
                          "bundle adjustment.")

        parser.add_argument(
            '--max-overlap-ratio',
            dest='maxOverlapRatio',
            default=0.85,
            type=float,
            help=
            'The maximum ratio of overlap between images to be accepted as part of a stereo pair. When floating intrinsics, this will be set to 1, to not upset some bookkeeping.'
        )

        parser.add_argument(
            '--stereo-arguments',
            dest='stereoArgs',
            # set --min-xcorr-level 0 to do the left-to-right
            # and right-to-left consistency check at the lowest level.
            default='--stereo-algorithm 2 --min-xcorr-level 0',
            help='Extra arguments to pass to stereo.')

        parser.add_argument('--start-frame', dest='startFrame', type=int,
                          default=icebridge_common.getSmallestFrame(),
                          help="Frame to start with.  Leave this and stop-frame blank to " + \
                          "process all frames.")
        parser.add_argument('--stop-frame',
                            dest='stopFrame',
                            type=int,
                            default=icebridge_common.getLargestFrame(),
                            help='Frame to stop on.')
        parser.add_argument(
            '--frames-file',
            dest='framesFile',
            default="",
            help=
            'Specific frames to run ortho2pinhole on within this frame range.')

        parser.add_argument('--max-num-lidar-to-fetch', dest='maxNumLidarToFetch', default=None,
                          type=int, help="The maximum number of lidar files to fetch. " + \
                          "This is used in debugging.")

        parser.add_argument("--camera-calibration-folder",
                            dest="inputCalFolder",
                            default=None,
                            help="The folder containing camera calibration.")

        parser.add_argument(
            "--input-calibration-camera",
            dest="inputCalCamera",
            default="",
            help=
            "Instead of looking up the calibrated camera in the calibration folder, use this one."
        )

        parser.add_argument(
            "--output-calibration-camera",
            dest="outputCalCamera",
            default="",
            help=
            "If specified, float the intrinsics and write the optimized model here."
        )

        parser.add_argument(
            "--output-model-type",
            dest="outputModelType",
            default="RPC",
            help="Generate a distortion model of type RPC, RPC5, or RPC6.")
        parser.add_argument(
            "--reference-dem-folder",
            dest="refDemFolder",
            default=None,
            help="The folder containing DEMs that created orthoimages.")

        parser.add_argument("--processing-subfolder",  dest="processingSubfolder", default=None,
                          help="Specify a subfolder name where the processing outputs will go. " + \
                          "fault is no additional folder")

        parser.add_argument(
            "--simple-cameras",
            action="store_true",
            dest="simpleCameras",
            default=False,
            help="Don't use orthoimages to refine the camera models.")

        # This option is only needed when generating camera models from the nav files.
        parser.add_argument(
            '--camera-mounting',
            default=0,
            dest='cameraMounting',
            type=int,
            help=
            '0=right-forwards, 1=left-forwards, 2=top-forwards, 3=bottom-forwards.'
        )

        # Performance options
        parser.add_argument(
            '--num-processes',
            dest='numProcesses',
            default=1,
            type=int,
            help='The number of simultaneous processes to run.')
        parser.add_argument(
            '--num-ortho-processes',
            dest='numOrthoProcesses',
            default=-1,
            type=int,
            help='The number of simultaneous ortho processes to run.')
        parser.add_argument('--num-threads',
                            dest='numThreads',
                            default=8,
                            type=int,
                            help='The number of threads per process.')

        # Action control
        parser.add_argument("--skip-fetch",
                            action="store_true",
                            dest="noFetch",
                            default=False,
                            help="Skip data fetching.")
        parser.add_argument("--skip-convert",
                            action="store_true",
                            dest="noConvert",
                            default=False,
                            help="Skip data conversion.")
        parser.add_argument("--stop-after-fetch",
                            action="store_true",
                            dest="stopAfterFetch",
                            default=False,
                            help="Stop program after data fetching.")
        parser.add_argument("--stop-after-convert",
                            action="store_true",
                            dest="stopAfterConvert",
                            default=False,
                            help="Stop program after data conversion.")
        parser.add_argument("--skip-validate",
                            action="store_true",
                            dest="skipValidate",
                            default=False,
                            help="Skip input data validation.")
        parser.add_argument("--ignore-missing-lidar",
                            action="store_true",
                            dest="ignoreMissingLidar",
                            default=False,
                            help="Keep going if the lidar is missing.")
        parser.add_argument(
            "--log-batches",
            action="store_true",
            dest="logBatches",
            default=False,
            help="Log the required batch commands without running them.")
        parser.add_argument(
            '--cleanup',
            action='store_true',
            default=False,
            dest='cleanup',
            help='If the final result is produced delete intermediate files.')
        parser.add_argument(
            '--many-ip',
            action='store_true',
            default=False,
            dest='manyip',
            help=
            'If to use a lot of IP in bundle adjustment from the beginning.')
        parser.add_argument(
            "--dry-run",
            action="store_true",
            dest="dryRun",
            default=False,
            help=
            "Set up the input directories but do not fetch/process any imagery."
        )

        parser.add_argument("--refetch", action="store_true", dest="reFetch", default=False,
                          help="Try fetching again if some files turned out invalid " + \
                          "during conversions.")
        parser.add_argument("--refetch-index",
                            action="store_true",
                            dest="refetchIndex",
                            default=False,
                            help="Force refetch of the index file.")
        parser.add_argument("--refetch-nav",
                            action="store_true",
                            dest="refetchNav",
                            default=False,
                            help="Force refetch of the nav file.")
        parser.add_argument("--stop-after-index-fetch",
                            action="store_true",
                            dest="stopAfterIndexFetch",
                            default=False,
                            help="Stop after fetching the indices.")

        parser.add_argument("--no-nav",
                            action="store_true",
                            dest="noNavFetch",
                            default=False,
                            help="Don't fetch or convert the nav data.")

        parser.add_argument("--no-lidar-convert",
                            action="store_true",
                            dest="noLidarConvert",
                            default=False,
                            help="Skip lidar files in the conversion step.")
        parser.add_argument(
            "--no-ortho-convert",
            action="store_true",
            dest="noOrthoConvert",
            default=False,
            help="Skip generating camera models in the conversion step.")
        parser.add_argument("--skip-fast-conversions",
                            action="store_true",
                            dest="skipFastConvert",
                            default=False,
                            help="Skips all non-ortho conversions.")

        options = parser.parse_args(argsIn)

    except argparse.ArgumentError as msg:
        parser.error(msg)

    icebridge_common.switchWorkDir()

    if options.numOrthoProcesses < 0:
        options.numOrthoProcesses = options.numProcesses

    isSouth = icebridge_common.checkSite(options.site)

    # Turned off elevation limits here since they are being set from LIDAR data.
    ## Add the site based elevation limits to the stereoArgs option
    #altLimits = icebridge_common.getElevationLimits(options.site)
    #options.stereoArgs = (' %s --elevation-limit %f %f '
    #                      % (options.stereoArgs, altLimits[0], altLimits[1]))
    options.stereoArgs = (' %s ' % (options.stereoArgs))

    if options.cameraLookupFile is None:
        options.cameraLookupFile = P.join(basepath, 'camera_lookup.txt')
    if not os.path.isfile(options.cameraLookupFile):
        raise Exception("Can't find camera file: " + options.cameraLookupFile)

    if len(options.yyyymmdd) != 8 and len(options.yyyymmdd) != 9:
        # Make an exception for 20100422a
        raise Exception("The --yyyymmdd field must have length 8 or 9.")

    if options.outputFolder is None:
        options.outputFolder = icebridge_common.outputFolder(
            options.site, options.yyyymmdd)

    if options.stopAfterIndexFetch:
        options.stopAfterFetch = True

    os.system('mkdir -p ' + options.outputFolder)
    logLevel = logging.INFO  # Record everything
    logger   = icebridge_common.setUpLogger(options.outputFolder, logLevel,
                                            'icebridge_processing_log_frames_' + \
                                            str(options.startFrame) + "_" + str(options.stopFrame))

    # Make sure we later know what we were doing
    logger.info("full_processing_script.py " + " ".join(argsIn))

    (out, err, status) = asp_system_utils.executeCommand(['uname', '-a'],
                                                         suppressOutput=True)
    logger.info("Running on machine: " + out)
    logger.info("Work dir is " + os.getcwd())

    os.system("ulimit -c 0")  # disable core dumps
    os.system("umask 022")  # enforce files be readable by others

    # Perform some input checks and initializations
    # These are not needed unless cameras are initialized
    if options.inputCalFolder is None or not os.path.exists(
            options.inputCalFolder):
        raise Exception("Missing camera calibration folder.")
    if options.refDemFolder is None or not os.path.exists(
            options.refDemFolder):
        raise Exception("Missing reference DEM folder.")

    refDemName = icebridge_common.getReferenceDemName(options.site)
    refDemPath = os.path.join(options.refDemFolder, refDemName)
    if not os.path.exists(refDemPath):
        raise Exception("Missing reference DEM: " + refDemPath)

    # TODO: CLEAN UP!!!
    # Set up the output folders
    cameraFolder = icebridge_common.getCameraFolder(options.outputFolder)
    imageFolder = icebridge_common.getImageFolder(options.outputFolder)
    jpegFolder = icebridge_common.getJpegFolder(options.outputFolder)
    orthoFolder = icebridge_common.getOrthoFolder(options.outputFolder)
    fireballFolder = icebridge_common.getFireballFolder(options.outputFolder)
    corrFireballFolder = icebridge_common.getCorrFireballFolder(
        options.outputFolder)
    lidarFolder = icebridge_common.getLidarFolder(options.outputFolder)
    navFolder = icebridge_common.getNavFolder(options.outputFolder)
    navCameraFolder = icebridge_common.getNavCameraFolder(options.outputFolder)
    processedFolder = icebridge_common.getProcessedFolder(options.outputFolder)

    if options.outputCalCamera != "":
        if options.maxOverlapRatio < 1:
            raise Exception ("For optimizing intrinsics, must set --max-overlap-ratio to 1, " + \
                             "to always use consecutive frames.")

        # Prepare to solve for intrinsics. Note that this modifies some things along the way.
        (options, cameraFolder, navCameraFolder, processedFolder) = \
                  solveIntrinsics_Part1(options, jpegFolder, cameraFolder, navCameraFolder,
                                        processedFolder, logger)

    # Handle subfolder option.  This is useful for comparing results with different parameters!
    if options.processingSubfolder:
        processedFolder = os.path.join(processedFolder,
                                       options.processingSubfolder)
        logger.info('Will write to processing subfolder: ' +
                    options.processingSubfolder)

    # If something failed in the first attempt either in fetch or in
    # convert, we will wipe bad files, and try to refetch/re-convert.
    numAttempts = 1
    if options.reFetch and (not options.noFetch):
        numAttempts = 2

    for attempt in range(numAttempts):
        if numAttempts > 1:
            logger.info("Fetch/convert attempt: " + str(attempt + 1))
        ans = runFetchConvert(options, isSouth, cameraFolder, imageFolder,
                              jpegFolder, orthoFolder, fireballFolder,
                              corrFireballFolder, lidarFolder, processedFolder,
                              navFolder, navCameraFolder, refDemPath, logger)
        if ans == 0:
            break

    if options.stopAfterFetch or options.dryRun or options.stopAfterConvert:
        logger.info('Fetch/convert finished!')
        return 0

    # Call the processing routine
    processTheRun(options, imageFolder, cameraFolder, lidarFolder, orthoFolder,
                  corrFireballFolder, processedFolder, isSouth, refDemPath)

    if options.outputCalCamera != "":
        # Finish solving for intrinscs.
        solveIntrinsics_Part2(options, imageFolder, cameraFolder, lidarFolder,
                              orthoFolder, processedFolder, isSouth, logger)