PhotoReport/exv.py

226 lines
8.8 KiB
Python
Executable File

#!/usr/bin/env python3
from subprocess import Popen, PIPE
from datetime import datetime
import json
from os.path import isfile, isdir
from os import walk
TYPES = [
"JPEG",
"JPG",
"EXV",
"CR2",
"CRW",
"MRW",
"TIFF",
"TIF",
"WEBP",
"DNG",
"NEF",
"FEF",
"ARW",
"RW2",
"SR2",
"SRW",
"ORF",
"PNG",
"PGF",
"RAF",
"PSD",
"JP2"
]
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 = [
"Image.Model",
"Photo.LensModel",
"Photo.FocalLength",
"Photo.ApertureValue",
"Photo.ExposureTime",
"Photo.ISOSpeedRatings",
"Photo.PixelXDimension",
"Photo.PixelYDimension",
"Image.ImageWidth",
"Image.ImageLength",
"Photo.DateTimeOriginal",
"Photo.DateTimeDigitized",
"Exif.Image.DateTime"
]
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 = {}
if not isfile(input_file):
raise ValueError("{} doesn't exist here.".format(input_file))
exif_dict.update({'file': input_file})
cmd = ["exiv2", "-Ptk", input_file]
with Popen(cmd, stdout=PIPE) as extracted_data:
extracted_data = extracted_data.stdout.read().decode()
for line in extracted_data.splitlines():
for exif in usefull_exif:
if exif in line.split()[0]:
exif_dict.update(
{
line.split()[0].split(".")[-1]:
" ".join(line.split()[1:])
}
)
for exif in usefull_exif:
if exif.split(".")[-1] not in exif_dict:
exif_dict.update({exif.split(".")[-1]: None})
if exif_dict['FocalLength']:
exif_dict['FocalLength'] = float(
exif_dict['FocalLength'].replace(" mm", ""))
if exif_dict['ApertureValue']:
exif_dict['ApertureValue'] = float(
exif_dict['ApertureValue'].replace("F", ""))
if exif_dict['PixelXDimension'] and exif_dict['PixelYDimension']:
exif_dict['PixelXDimension'] = int(exif_dict['PixelXDimension'])
exif_dict['PixelYDimension'] = int(exif_dict['PixelYDimension'])
pixels = exif_dict['PixelXDimension'] * \
exif_dict['PixelYDimension']
elif exif_dict['ImageLength'] and exif_dict['ImageWidth']:
exif_dict['PixelXDimension'] = int(exif_dict['ImageLength'])
exif_dict['PixelYDimension'] = int(exif_dict['ImageWidth'])
pixels = exif_dict['PixelXDimension'] * \
exif_dict['PixelYDimension']
else:
pixels = None
if exif_dict['DateTimeOriginal']:
exif_dict['DateTime'] = datetime.strptime(
exif_dict['DateTimeOriginal'], "%Y:%m:%d %H:%M:%S")
elif exif_dict['DateTimeDigitized']:
exif_dict['DateTime'] = datetime.strptime(
exif_dict['DateTimeDigitized'], "%Y:%m:%d %H:%M:%S")
elif exif_dict['DateTime']:
exif_dict['DateTime'] = datetime.strptime(
exif_dict['DateTime'], "%Y:%m:%d %H:%M:%S")
else:
exif_dict['DateTime'] = None
if exif_dict['ExposureTime']:
try:
exif_dict['ExposureTime'] = float(
exif_dict['ExposureTime'].split(" ")[0])
except ValueError:
exposure = exif_dict['ExposureTime'].split(" ")[0]
exif_dict['ExposureTime'] = float(
1/(float(exposure.split("/")[1])))
if pixels:
exif_dict.update({'Dimension': round((pixels/10**6), 1)})
else:
exif_dict.update({'Dimension': None})
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))