#!/usr/bin/env python3 import piexif from datetime import datetime import json from os.path import isfile, isdir from os import walk TYPES = [ "JPEG", "JPG", "CR2", "TIFF", "TIF", "DNG", "NEF", ] def extractor(input_files, start, end, recursive, extensions): if extensions: extensions = [ext.upper() for ext in extensions] if not isinstance(input_files, type(list())): raise ValueError("Input files must be a list.") exif_dict_list = [] files = [] usefull_exif = { "Model": "", "LensModel": "", "FocalLength": 0, "ApertureValue": 0, "ExposureTime": 0, "ISOSpeedRatings": 0, "PixelXDimension": 0, "PixelYDimension": 0, "ImageWidth": 0, "ImageLength": 0, "Pixels": None, "Dimension": 0, "DateTime": None, } for item in input_files: if isdir(item): for (dirpath, dirnames, filenames) in walk(item): for filename in filenames: if filename.split(".")[-1].upper() in TYPES: files.append("{}/{}".format(dirpath, filename)) if not recursive: break else: files.append(item) if extensions: files_with_ext_filter = [] for filename in files: if filename.split(".")[-1].upper() in extensions: files_with_ext_filter.append("{}".format(filename)) input_files = files_with_ext_filter else: input_files = files for input_file in input_files: exif_dict = usefull_exif if not isfile(input_file): raise ValueError("{} doesn't exist here.".format(input_file)) exif_dict.update({'file': input_file}) exif_tags = piexif.load(input_file) if 0x0110 in exif_tags['0th']: exif_dict.update({'Model': exif_tags['0th'][0x0110].decode()}) if 0xa434 in exif_tags['Exif']: exif_dict.update({'LensModel': exif_tags['Exif'][0xa434].decode().split("\x00")[0]}) if 0x920a in exif_tags['Exif']: exif_dict.update({'FocalLength': float(exif_tags['Exif'][0x920a][0] / exif_tags['Exif'][0x920a][1])}) if 0x829d in exif_tags['Exif']: exif_dict.update({'ApertureValue': float(exif_tags['Exif'][0x829d][0]/exif_tags['Exif'][0x829d][1])}) if 0x829a in exif_tags['Exif']: exif_dict.update({'ExposureTime': float(exif_tags['Exif'][0x829a][0]/exif_tags['Exif'][0x829a][1])}) if 0x8827 in exif_tags['Exif']: exif_dict.update({'ISOSpeedRatings': int(exif_tags['Exif'][0x8827])}) if 0xa002 in exif_tags['Exif'] and 0xa003 in exif_tags['Exif']: exif_dict.update({'Pixels': int(exif_tags['Exif'][0xa002]) * int(exif_tags['Exif'][0xa003])}) elif 0x0100 in exif_tags['0th'] and 0x0101 in exif_tags['0th']: exif_dict.update({'Pixels': int(exif_tags['0th'][0x0100]) * int(exif_tags['0th'][0x0101])}) if 0x9003 in exif_tags['Exif']: exif_dict['DateTime'] = datetime.strptime( exif_tags['Exif'][0x9003].decode(), "%Y:%m:%d %H:%M:%S") elif 0x9004 in exif_tags['Exif']: exif_dict['DateTime'] = datetime.strptime( exif_tags['Exif'][0x9004].decode(), "%Y:%m:%d %H:%M:%S") elif 0x0132 in exif_tags['0th']: exif_dict['DateTime'] = datetime.strptime( exif_tags['0th'][0x0132].decode(), "%Y:%m:%d %H:%M:%S") if exif_dict['Pixels']: exif_dict.update({'Dimension': round((exif_dict['Pixels']/10**6), 1)}) exif_dict_list.append(exif_dict) cameras, lenses, focals, apertures, exposures = {}, {}, {}, {}, {} isos, dimensions, cameras_lenses, dates = {}, {}, {}, {} cameras_list, lenses_list, focals_list, apertures_list, exposures_list = [], [], [], [], [] isos_list, dimensions_list, cameras_lenses_list, dates_list = [], [], [], [] for data in exif_dict_list: if data['DateTime'] and start <= data['DateTime'] <= end: cameras_list.append(data['Model']) lenses_list.append(data['LensModel']) focals_list.append(data['FocalLength']) apertures_list.append(data['ApertureValue']) exposures_list.append(data['ExposureTime']) isos_list.append(data['ISOSpeedRatings']) dimensions_list.append(data['Dimension']) if data['LensModel']: cameras_lenses_list.append( "{} + {}".format(data['Model'], data['LensModel'])) dates_list.append(data['DateTime'].strftime("%Y%m%d")) for camera in list(set(cameras_list)): if camera: cameras.update({camera: cameras_list.count(camera)}) for lens in list(set(lenses_list)): if lens: lenses.update({lens: lenses_list.count(lens)}) for camera_lens in list(set(cameras_lenses_list)): if camera_lens: cameras_lenses.update( {camera_lens: cameras_lenses_list.count(camera_lens)}) for focal in list(set(focals_list)): if focal: focals.update({focal: focals_list.count(focal)}) for aperture in list(set(apertures_list)): if aperture: apertures.update({aperture: apertures_list.count(aperture)}) for exposure in list(set(exposures_list)): if exposure: exposures.update( {float(exposure*1000): exposures_list.count(exposure)}) for iso in list(set(isos_list)): if iso: isos.update({iso: isos_list.count(iso)}) for dimension in list(set(dimensions_list)): if dimension: dimensions.update({dimension: dimensions_list.count(dimension)}) for date in list(set(dates_list)): if date: dates.update({date: dates_list.count(date)}) return { "total": len(dimensions_list), "date": dates, "cameras": cameras, "lenses": lenses, "cameras+lenses": cameras_lenses, "focals": focals, "apertures": apertures, "shutter_speeds": exposures, "isos": isos, "dimensions": dimensions } if __name__ == '__main__': import argparse parser = argparse.ArgumentParser() parser.add_argument( "infile", help="input file (EXV, JPG, CR2, ...). Can be more than one.", nargs='+') parser.add_argument("-s", "--start-date", help="start date as YYYYMMDD. If omitted, begin of the collection.", const=None) parser.add_argument("-e", "--end-date", help="end date as YYYYMMDD. If omitted, today", const=None) parser.add_argument("-R", "--recursive", help="Walk throught directory to find all files", action='store_true') parser.add_argument("-x", "--extension", help="Read files with this extension only (can be specified more than one time)", action='append') args = parser.parse_args() if not args.recursive: recursive = False if not args.extension: extensions = None else: extensions = args.extension if not args.start_date: args.start_date = "18250101" # Ok, that's the year of the 1st photography by Nicéphore Niépce. Shoul'd be enough for a start date. if not args.end_date: args.end_date = datetime.strftime(datetime.now(), "%Y%m%d") try: datetime.strptime(args.start_date, "%Y%m%d") datetime.strptime(args.end_date, "%Y%m%d") except ValueError: print("Date must be YYYYMMDD.") exit() recursive = args.recursive print(json.dumps(extractor(args.infile, datetime.strptime(args.start_date, "%Y%m%d"), datetime.strptime(args.end_date, "%Y%m%d"), recursive, extensions), indent=2, sort_keys=True))