def getArchivedImages(constants, cameras, startTimeDT, timeRangeSeconds, minusMinutes): """Get random images from HPWREN archive matching given constraints and optionally subtract them Args: constants (dict): "global" contants cameras (list): list of cameras startTimeDT (datetime): starting time of time range timeRangeSeconds (int): number of seconds in time range minusMinutes (int): number of desired minutes between images to subract Returns: Tuple containing camera name, current timestamp, filepath of regular image, and filepath of difference image """ if getArchivedImages.tmpDir == None: getArchivedImages.tmpDir = tempfile.TemporaryDirectory() logging.warning('TempDir %s', getArchivedImages.tmpDir.name) cameraID = cameras[int(len(cameras) * random.random())]['name'] timeDT = startTimeDT + datetime.timedelta(seconds=random.random() * timeRangeSeconds) if minusMinutes: prevTimeDT = timeDT + datetime.timedelta(seconds=-60 * minusMinutes) else: prevTimeDT = timeDT files = img_archive.getHpwrenImages(constants['googleServices'], settings, getArchivedImages.tmpDir.name, constants['camArchives'], cameraID, prevTimeDT, timeDT, minusMinutes or 1) # logging.warning('files %s', str(files)) if not files: return (None, None, None, None) if minusMinutes: if len(files) > 1: if files[0] >= files[ 1]: # files[0] is supposed to be earlier than files[1] logging.warning('unexpected file order %s', str(files)) for file in files: os.remove(file) return (None, None, None, None) imgDiffPath = genDiffImage(files[1], files[0], minusMinutes) os.remove(files[0]) # no longer needed parsedName = img_archive.parseFilename(files[1]) return (cameraID, parsedName['unixTime'], files[1], imgDiffPath) else: logging.warning('unexpected file count %s', str(files)) for file in files: os.remove(file) return (None, None, None, None) elif len(files) > 0: parsedName = img_archive.parseFilename(files[0]) return (cameraID, parsedName['unixTime'], files[0], files[0]) return (None, None, None, None)
def main(): reqArgs = [ ["c", "cameraID", "ID (code name) of camera"], [ "s", "startTime", "starting date and time in ISO format (e.g., 2019-02-22T14:34:56 in Pacific time zone)" ], ] optArgs = [ [ "e", "endTime", "ending date and time in ISO format (e.g., 2019-02-22T14:34:56 in Pacific time zone)" ], [ "g", "gapMinutes", "override default of 1 minute gap between images to download" ], ["o", "outputDir", "directory to save the output image"], ] args = collect_args.collectArgs( reqArgs, optionalArgs=optArgs, parentParsers=[goog_helper.getParentParser()]) googleServices = goog_helper.getGoogleServices(settings, args) gapMinutes = int(args.gapMinutes) if args.gapMinutes else 1 outputDir = args.outputDir if args.outputDir else settings.downloadDir startTimeDT = dateutil.parser.parse(args.startTime) if args.endTime: endTimeDT = dateutil.parser.parse(args.endTime) else: endTimeDT = startTimeDT assert startTimeDT.year == endTimeDT.year assert startTimeDT.month == endTimeDT.month assert startTimeDT.day == endTimeDT.day assert endTimeDT >= startTimeDT camArchives = img_archive.getHpwrenCameraArchives(googleServices['sheet'], settings) files = img_archive.getHpwrenImages(googleServices, settings, outputDir, camArchives, args.cameraID, startTimeDT, endTimeDT, gapMinutes) if files: logging.warning('Found %d files.', len(files)) else: logging.error('No matches for camera ID %s', args.cameraID)
def emailFireNotification(constants, cameraID, imgPath, annotatedFile, driveFileIDs, fireSegment, timestamp): """Send an email alert for a potential new fire Send email with information about the camera and fire score includeing image attachments Args: constants (dict): "global" contants cameraID (str): camera name imgPath: filepath of the original image annotatedFile: filepath of the annotated image driveFileIDs (list): List of Google drive IDs for the uploaded image files fireSegment (dictionary): dictionary with information for the segment with fire/smoke timestamp (int): time.time() value when image was taken """ dbManager = constants['dbManager'] subject = 'Possible (%d%%) fire in camera %s' % (int( fireSegment['score'] * 100), cameraID) body = 'Please check the attached images for fire.' # commenting out links to google drive because they appear as extra attachments causing confusion # and some email recipients don't even have permissions to access drive. # for driveFileID in driveFileIDs: # driveTempl = '\nAlso available from google drive as https://drive.google.com/file/d/%s' # driveBody = driveTempl % driveFileID # body += driveBody # emails are sent from settings.fuegoEmail and bcc to everyone with active emails in notifications SQL table dbResult = dbManager.getNotifications(filterActiveEmail=True) emails = [x['email'] for x in dbResult] if len(emails) > 0: # attach images spanning a few minutes so reviewers can evaluate based on progression startTimeDT = datetime.datetime.fromtimestamp(timestamp - 3 * 60) endTimeDT = datetime.datetime.fromtimestamp(timestamp - 1 * 60) with tempfile.TemporaryDirectory() as tmpDirName: oldImages = img_archive.getHpwrenImages( constants['googleServices'], settings, tmpDirName, constants['camArchives'], cameraID, startTimeDT, endTimeDT, 1) oldImages = oldImages or [] attachments = oldImages + [imgPath, annotatedFile] email_helper.sendEmail(constants['googleServices']['mail'], settings.fuegoEmail, emails, subject, body, attachments)
def main(): reqArgs = [ ["o", "outputDir", "local directory to save images segments"], ["i", "inputCsv", "csvfile with contents of Fuego Cropped Images"], ] optArgs = [ ["s", "startRow", "starting row"], ["e", "endRow", "ending row"], ["d", "display", "(optional) specify any value to display image and boxes"], ["x", "minDiffX", "(optional) override default minDiffX of 299"], ["y", "minDiffY", "(optional) override default minDiffY of 299"], ["a", "minArea", "(optional) override default throw away areas < 1/100 of 299x299"], ["t", "throwSize", "(optional) override default throw away size of 1000x1000"], ["g", "growRatio", "(optional) override default grow ratio of 1.2"], ["m", "minusMinutes", "(optional) subtract images from given number of minutes ago"], ] args = collect_args.collectArgs(reqArgs, optionalArgs=optArgs, parentParsers=[goog_helper.getParentParser()]) startRow = int(args.startRow) if args.startRow else 0 endRow = int(args.endRow) if args.endRow else 1e9 minDiffX = int(args.minDiffX) if args.minDiffX else 299 minDiffY = int(args.minDiffY) if args.minDiffY else 299 throwSize = int(args.throwSize) if args.throwSize else 1000 growRatio = float(args.growRatio) if args.growRatio else 1.2 minArea = int(args.minArea) if args.minArea else int(299*2.99) minusMinutes = int(args.minusMinutes) if args.minusMinutes else 0 googleServices = goog_helper.getGoogleServices(settings, args) camArchives = img_archive.getHpwrenCameraArchives(googleServices['sheet'], settings) if minusMinutes: timeGapDelta = datetime.timedelta(seconds = 60*minusMinutes) cameraCache = {} skippedTiny = [] skippedHuge = [] skippedArchive = [] with open(args.inputCsv) as csvFile: csvreader = csv.reader(csvFile) for (rowIndex, csvRow) in enumerate(csvreader): if rowIndex < startRow: continue if rowIndex > endRow: print('Reached end row', rowIndex, endRow) break [cropName, minX, minY, maxX, maxY, fileName] = csvRow[:6] minX = int(minX) minY = int(minY) maxX = int(maxX) maxY = int(maxY) oldCoords = (minX, minY, maxX, maxY) if ((maxX - minX) > throwSize) and ((maxY - minY) > throwSize): logging.warning('Skip large image: dx=%d, dy=%d, name=%s', maxX - minX, maxY - minY, fileName) skippedHuge.append((rowIndex, fileName, maxX - minX, maxY - minY)) continue if ((maxX - minX) * (maxY - minY)) < minArea: logging.warning('Skipping tiny image with area: %d, name=%s', (maxX - minX) * (maxY - minY), fileName) skippedTiny.append((rowIndex, fileName, (maxX - minX) * (maxY - minY))) continue # get base image from google drive that was uploaded by sort_images.py dirID = getCameraDir(googleServices['drive'], cameraCache, fileName)#-##REPLACE DEP. GDRIVE W HPREWN# localFilePath = os.path.join(settings.downloadDir, fileName)#sets a path for that image() not yet downloadedby this iteration print('local', localFilePath) if not os.path.isfile(localFilePath):# if file has not been downloaded by a previous iteration print('download', fileName) #+##REPLACE DEP. GDRIVE W HPREWN#nameParsed = img_archive.parseFilename(fileName)#parses file name into dictionary of parts name,unixtime,etc. #+##REPLACE DEP. GDRIVE W HPREWN#matchingCams = list(filter(lambda x: nameParsed['cameraID'] == x['id'], camArchives))#filter through camArchives for ids matching cameraid #+##REPLACE DEP. GDRIVE W HPREWN#if len(matchingCams) != 1:#if we cannot determine where the image will come from we cannot use the image #+##REPLACE DEP. GDRIVE W HPREWN# logging.warning('Skipping camera without archive: %d, %s', len(matchingCams), str(matchingCams)) #+##REPLACE DEP. GDRIVE W HPREWN# skippedArchive.append((rowIndex, fileName, matchingCams)) #+##REPLACE DEP. GDRIVE W HPREWN# continue #+##REPLACE DEP. GDRIVE W HPREWN#archiveDirs = matchingCams[0]['dirs'] #+##REPLACE DEP. GDRIVE W HPREWN#logging.warning('Found %s directories', archiveDirs) #+##REPLACE DEP. GDRIVE W HPREWN#time = datetime.datetime.fromtimestamp(nameParsed['unixTime']) #+##REPLACE DEP. GDRIVE W HPREWN#for dirName in archiveDirs:#search directories of camera for a time near #+##REPLACE DEP. GDRIVE W HPREWN# logging.warning('Searching for files in dir %s', dirName) #+##REPLACE DEP. GDRIVE W HPREWN# imgPaths = img_archive.downloadFilesHpwren(settings.downloadDir, nameParsed['cameraID'], dirName, time, time, 1, 0) #+##REPLACE DEP. GDRIVE W HPREWN# if imgPaths: #+##REPLACE DEP. GDRIVE W HPREWN# localFilePath = imgPaths[0] #+##REPLACE DEP. GDRIVE W HPREWN# break #+##REPLACE DEP. GDRIVE W HPREWN#if not imgPaths: #+##REPLACE DEP. GDRIVE W HPREWN# logging.warning('Skipping image not found: %s', fileName) #+##REPLACE DEP. GDRIVE W HPREWN# skippedArchive.append((rowIndex, fileName, time))#archive that images were skipped #+##REPLACE DEP. GDRIVE W HPREWN# continue goog_helper.downloadFile(googleServices['drive'], dirID, fileName, localFilePath)#-##REPLACE DEP. GDRIVE W HPREWN# imgOrig = Image.open(localFilePath)#opens image # if in subracted images mode, download an earlier image and subtract if minusMinutes: nameParsed = img_archive.parseFilename(fileName)#parses file name into dictionary of parts name,unixtime,etc. dt = datetime.datetime.fromtimestamp(nameParsed['unixTime']) dt -= timeGapDelta earlierImgPath = None files = img_archive.getHpwrenImages(googleServices, settings, settings.downloadDir, camArchives, nameParsed['cameraID'], dt, dt, 1) if files: earlierImgPath = files[0] else: logging.warning('Skipping image without prior image: %s, %s', str(dt), fileName) skippedArchive.append((rowIndex, fileName, dt)) continue logging.warning('Subtracting old image %s', earlierImgPath) earlierImg = Image.open(earlierImgPath) diffImg = img_archive.diffImages(imgOrig, earlierImg) # realImgOrig = imgOrig # is this useful? imgOrig = diffImg fileNameParts = os.path.splitext(fileName) fileName = str(fileNameParts[0]) + ('_Diff%d' % minusMinutes) + fileNameParts[1] # crop the full sized image to show just the smoke, but shifted and flipped # shifts and flips increase number of segments for training and also prevent overfitting by perturbing data cropCoords = getCropCoords((minX, minY, maxX, maxY), minDiffX, minDiffY, growRatio, (imgOrig.size[0], imgOrig.size[1])) for newCoords in cropCoords: # XXXX - save work if old=new? print('coords old,new', oldCoords, newCoords) imgNameNoExt = str(os.path.splitext(fileName)[0]) cropImgName = imgNameNoExt + '_Crop_' + 'x'.join(list(map(lambda x: str(x), newCoords))) + '.jpg' cropImgPath = os.path.join(args.outputDir, cropImgName) cropped_img = imgOrig.crop(newCoords) cropped_img.save(cropImgPath, format='JPEG') flipped_img = cropped_img.transpose(Image.FLIP_LEFT_RIGHT) flipImgName = imgNameNoExt + '_Crop_' + 'x'.join(list(map(lambda x: str(x), newCoords))) + '_Flip.jpg' flipImgPath = os.path.join(args.outputDir, flipImgName) flipped_img.save(flipImgPath, format='JPEG') print('Processed row: %s, file: %s' % (rowIndex, fileName)) if args.display: displayCoords = [oldCoords] + cropCoords displayImageWithScores(imgOrig, displayCoords) imageDisplay(imgOrig) logging.warning('Skipped tiny images %d, %s', len(skippedTiny), str(skippedTiny)) logging.warning('Skipped huge images %d, %s', len(skippedHuge), str(skippedHuge)) logging.warning('Skipped images without archives %d, %s', len(skippedArchive), str(skippedArchive))