190 lines
7.8 KiB
Python
Executable File
190 lines
7.8 KiB
Python
Executable File
#!/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))
|