def packAndSendCameraFolder(run, logger): '''Archive the camera folder for later use''' logger.info('Archiving camera folder for run ' + str(run)) # Tar up the camera files and send them at the same time using the shiftc command cameraFolder = run.getCameraFolder() fileName = run.getCameraTarName() lfePath = os.path.join(REMOTE_CAMERA_FOLDER, fileName) # First remove any existing tar file. Use 2>/dev/null to not print # NASA's "You have no expecation of privacy." cmd = "ssh lfe 'rm -f " + stripHost(lfePath) + "' 2>/dev/null" logger.info(cmd) os.system(cmd) # Do the new file. Save the projection bounds, we will need that for later # as that file is very time consuming to create. cwd = os.getcwd() os.chdir(run.parentFolder) runFolder = str(run) cmd = 'shiftc --wait -d -r --include=\'^.*?(' + \ os.path.basename(icebridge_common.projectionBoundsFile(runFolder)) + \ '|' + icebridge_common.validFilesPrefix() + '.*|' + \ os.path.basename(cameraFolder) + '.*?\.tsai)$\' --create-tar ' + runFolder + \ ' ' + lfePath robust_shiftc(cmd, logger) logger.info("Finished archiving cameras.") os.chdir(cwd)
def getImageSpacing(orthoFolder, availableFrames, startFrame, stopFrame, forceAllFramesInRange): '''Find a good image stereo spacing interval that gives us a good balance between coverage and baseline width. Also detect all frames where this is a large break after the current frame.''' logger.info('Computing optimal image stereo interval...') ## With very few cameras this is the only possible way to process them #if len(availableFrames) < 3 and not forceAllFramesInRange: # return ([], {}) # No skip, no breaks # Retrieve a list of the ortho files orthoIndexPath = icebridge_common.csvIndexFile(orthoFolder) if not os.path.exists(orthoIndexPath): raise Exception("Error: Missing ortho index file: " + orthoIndexPath + ".") (orthoFrameDict, orthoUrlDict) = icebridge_common.readIndexFile(orthoIndexPath, prependFolder = True) # From the dictionary create a sorted list of ortho files in the frame range breaks = [] largeSkips = {} orthoFiles = [] for frame in sorted(orthoFrameDict.keys()): # Only process frames within the range if not ( (frame >= startFrame) and (frame <= stopFrame) ): continue orthoPath = orthoFrameDict[frame] frame = icebridge_common.getFrameNumberFromFilename(orthoPath) if not forceAllFramesInRange: if frame not in availableFrames: # Skip frames we did not compute a camera for continue orthoFiles.append(orthoPath) orthoFiles.sort() numOrthos = len(orthoFiles) # First load whatever boxes are there projectionIndexFile = icebridge_common.projectionBoundsFile(os.path.dirname(orthoFolder)) logger.info("Reading: " + projectionIndexFile) boundsDict = icebridge_common.readProjectionBounds(projectionIndexFile) # Get the bounding box and frame number of each ortho image logger.info('Loading bounding boxes...') frames = [] updatedBounds = False # will be true if some computation got done count = 0 for i in range(0, numOrthos): # This can be slow, so add a progress dialong count = count + 1 if (count - 1) % 1000 == 0: logger.info('Progress: ' + str(count) + '/' + str(numOrthos)) thisFrame = icebridge_common.getFrameNumberFromFilename(orthoFiles[i]) if thisFrame not in boundsDict: imageGeoInfo = asp_geo_utils.getImageGeoInfo(orthoFiles[i], getStats=False) thisBox = imageGeoInfo['projection_bounds'] boundsDict[thisFrame] = thisBox updatedBounds = True frames.append(thisFrame) # Read this file again, in case some other process modified it in the meantime. # This won't happen in production mode, but can during testing with partial sequences. boundsDictRecent = icebridge_common.readProjectionBounds(projectionIndexFile) for frame in sorted(boundsDictRecent.keys()): if not frame in boundsDict.keys(): boundsDict[frame] = boundsDictRecent[frame] updatedBounds = True # Save the bounds. There is always the danger that two processes will # do that at the same time, but this is rare, as hopefully we get here # only once from the manager. It is not a big loss if this file gets messed up. if updatedBounds: logger.info("Writing: " + projectionIndexFile) icebridge_common.writeProjectionBounds(projectionIndexFile, boundsDict) # Since we are only comparing the image bounding boxes, not their exact corners, # these ratios are only estimates. MAX_RATIO = 0.85 # Increase skip until we get below this... MIN_RATIO = 0.75 # ... but don't go below this value! NOTRY_RATIO = 0.0001 # Don't bother with overlap amounts less than this (small on purpose) def getBboxArea(bbox): '''Return the area of a bounding box in form of (minX, maxX, minY, maxY)''' width = bbox[1] - bbox[0] height = bbox[3] - bbox[2] if (width < 0) or (height < 0): return 0 return width*height # Iterate over the frames and find the best stereo frame for each for i in range(0, numOrthos-1): thisFrame = frames[i] thisBox = boundsDict[thisFrame] thisArea = getBboxArea(thisBox) interval = 1 while(True): # Compute intersection area between this and next image nextFrame = frames[i+interval] nextBox = boundsDict[nextFrame] intersect = [max(thisBox[0], nextBox[0]), # Min X min(thisBox[1], nextBox[1]), # Max X max(thisBox[2], nextBox[2]), # Min Y min(thisBox[3], nextBox[3])] # Max Y area = getBboxArea(intersect) ratio = 0 if area > 0: ratio = area / thisArea if interval == 1: # Cases for the smallest interval... if ratio < NOTRY_RATIO: breaks.append(thisFrame) # No match for this frame logger.info('Detected large break after frame ' + str(thisFrame)) break if ratio < MIN_RATIO: break # No reason to try increasing skip amounts for this frame else: # interval > 1 if ratio < MIN_RATIO: # Went too small, walk back the interval. interval = interval - 1 break if ratio > MAX_RATIO: # Too much overlap, increase interval interval = interval + 1 else: # Overlap is fine, keep this interval. break # Handle the case where we go past the end of frames looking for a match. if i+interval >= len(frames): interval = interval - 1 break if interval > 1: # Only record larger than normal intervals. largeSkips[thisFrame] = interval logger.info('Detected ' + str(len(breaks)) + ' breaks in image coverage.') logger.info('Detected ' + str(len(largeSkips)) + ' images with interval > 1.') return (breaks, largeSkips)