import ecto import cv2 import pyexiv2 from functools import partial from multiprocessing import Pool from opendm import log from opendm import system from opendm import io from opendm import types def resize(src_dir, target_dir, resize_to, rerun_cell, photo): # define image paths path_file = photo.path_file new_path_file = io.join_paths(target_dir, photo.filename) # set raw image path in case we want to rerun cell if io.file_exists(new_path_file) and rerun_cell: path_file = io.join_paths(src_dir, photo.filename) if not io.file_exists(new_path_file) or rerun_cell: # open and resize image with opencv img = cv2.imread(path_file) # compute new size max_side = max(img.shape[0], img.shape[1]) if max_side <= resize_to: log.ODM_WARNING('Resize parameter is greater than or equal to the largest side of the image') ratio = float(resize_to) / float(max_side) img_r = cv2.resize(img, None, fx=ratio, fy=ratio) # write image with opencv cv2.imwrite(new_path_file, img_r) # read metadata with pyexiv2 old_meta = pyexiv2.ImageMetadata(path_file) new_meta = pyexiv2.ImageMetadata(new_path_file) old_meta.read() new_meta.read() # copy metadata old_meta.copy(new_meta) # update metadata size new_meta['Exif.Photo.PixelXDimension'] = img_r.shape[0] new_meta['Exif.Photo.PixelYDimension'] = img_r.shape[1] new_meta.write() # update photos array with new values photo.path_file = new_path_file photo.width = img_r.shape[0] photo.height = img_r.shape[1] photo.update_focal() # log message log.ODM_DEBUG('Resized %s | dimensions: %s' % (photo.filename, img_r.shape)) else: # log message log.ODM_WARNING('Already resized %s | dimensions: %s x %s' % (photo.filename, photo.width, photo.height)) return photo class ODMResizeCell(ecto.Cell): def declare_params(self, params): params.declare("resize_to", "resizes images by the largest side", 2400) def declare_io(self, params, inputs, outputs): inputs.declare("tree", "Struct with paths", []) inputs.declare("args", "The application arguments.", []) inputs.declare("photos", "Clusters inputs. list of ODMPhoto's", []) outputs.declare("photos", "Clusters output. list of ODMPhoto's", []) def process(self, inputs, outputs): # Benchmarking start_time = system.now_raw() log.ODM_INFO('Running ODM Resize Cell') # get inputs args = self.inputs.args tree = self.inputs.tree photos = self.inputs.photos if not photos: log.ODM_ERROR('Not enough photos in photos to resize') return ecto.QUIT if self.params.resize_to <= 0: log.ODM_ERROR('Resize parameter must be greater than 0') return ecto.QUIT # create working directory system.mkdir_p(tree.dataset_resize) log.ODM_DEBUG('Resizing dataset to: %s' % tree.dataset_resize) # check if we rerun cell or not rerun_cell = (args.rerun is not None and args.rerun == 'resize') or \ (args.rerun_all) or \ (args.rerun_from is not None and 'resize' in args.rerun_from) # loop over photos photos = Pool().map( partial(resize, tree.dataset_raw, tree.dataset_resize, self.params.resize_to, rerun_cell), photos ) log.ODM_INFO('Resized %s images' % len(photos)) # append photos to cell output self.outputs.photos = photos if args.time: system.benchmark(start_time, tree.benchmarking, 'Resizing') log.ODM_INFO('Running ODM Resize Cell - Finished') return ecto.OK if args.end_with != 'resize' else ecto.QUIT