11 Commits
v0.0.1 ... v0.1

3 changed files with 113 additions and 26 deletions

17
README
View File

@ -1,17 +0,0 @@
===========
PhotoReport
===========
Dependencies
============
- Python 3 (and some additionnals modules, see 'requirements.txt')
- rst2pdf
- ImageMagick
Howto install ?
===============
- create a virtualenv with python3
- install python requirements
- uwsgi and stuff.... (TODO)

38
README.md Normal file
View File

@ -0,0 +1,38 @@
# PhotoReport
## Dependencies
- Python 3 (and some additionnals modules, see 'requirements.txt')
- rst2pdf
- ImageMagick
- exiv2 (for direct exif reading)
## Howto install ?
- create a virtualenv with python3
- install python requirements
## CLI usage
[todo]
## Comment contribuer ?
- Cloner le dépôt : `git clone https://git.antoineve.me/PhotoReport/`
- Créer une branche : `git checkout -b <new_feature>`
- Coder et commiter : `git commit -a -m <description>`
- Créer le patch : `git format-patch $(git merge-base --fork-point master)..<new_feature>`
- M'envoyer le patch par mail : [antoine+photoreport@van-elstraete.net](mailto:antoine+photoreport@van-elstraete.net)
- J'applique le patch avec `git am --signoff -k < <new_feature>.patch`
## BUGS
For the moment, PDF report isn't working.
## Features requests
- Est-ce quil sera possible de filtrer par objectif/boîtier, parce quau final, les stats de focale/ouvertures sont surtout pertinentes en isolant un seul objectif (dans le cas des zooms) ?
- En effet, loption -d serait utile, voire directement automatiser la recherche récursive
- dédupliquer si le nom est le même avec une extension différente
- exv.py -d ./2019 -u -e NEF -e CR2 -e DNG -e JPG (Pour le dossier 2019, éviter les doubles, uniquement les extensions NEF, CR2, DNG, JPG, par exemple.)
- Sortie exploitable par LaTeX

84
exv.py
View File

@ -4,13 +4,41 @@
from subprocess import Popen, PIPE from subprocess import Popen, PIPE
from datetime import datetime from datetime import datetime
import json import json
from os.path import isfile 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): def extractor(input_files, start, end, recursive, extensions):
if extensions:
extensions = [ext.upper() for ext in extensions]
if not isinstance(input_files, type(list())): if not isinstance(input_files, type(list())):
raise ValueError("Input files must be a list.") raise ValueError("Input files must be a list.")
exif_dict_list = [] exif_dict_list = []
files = []
usefull_exif = [ usefull_exif = [
"Image.Model", "Image.Model",
"Photo.LensModel", "Photo.LensModel",
@ -22,8 +50,29 @@ def extractor(input_files, start, end):
"Photo.PixelYDimension", "Photo.PixelYDimension",
"Image.ImageWidth", "Image.ImageWidth",
"Image.ImageLength", "Image.ImageLength",
"Photo.DateTimeOriginal" "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: for input_file in input_files:
exif_dict = {} exif_dict = {}
if not isfile(input_file): if not isfile(input_file):
@ -35,9 +84,9 @@ def extractor(input_files, start, end):
extracted_data = extracted_data.stdout.read().decode() extracted_data = extracted_data.stdout.read().decode()
if extracted_data: if extracted_data:
extracted_data = extracted_data.splitlines()[0] extracted_data = extracted_data.splitlines()[0]
exif_dict.update({extracted.split(".")[1]: extracted_data}) exif_dict.update({extracted.split(".")[-1]: extracted_data})
else: else:
exif_dict.update({extracted.split(".")[1]: None}) exif_dict.update({extracted.split(".")[-1]: None})
if exif_dict['FocalLength']: if exif_dict['FocalLength']:
exif_dict['FocalLength'] = float( exif_dict['FocalLength'] = float(
exif_dict['FocalLength'].replace(" mm", "")) exif_dict['FocalLength'].replace(" mm", ""))
@ -57,8 +106,16 @@ def extractor(input_files, start, end):
else: else:
pixels = None pixels = None
if exif_dict['DateTimeOriginal']: if exif_dict['DateTimeOriginal']:
exif_dict['DateTimeOriginal'] = datetime.strptime( exif_dict['DateTime'] = datetime.strptime(
exif_dict['DateTimeOriginal'], "%Y:%m:%d %H:%M:%S") 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']: if exif_dict['ExposureTime']:
try: try:
exif_dict['ExposureTime'] = float( exif_dict['ExposureTime'] = float(
@ -77,7 +134,7 @@ def extractor(input_files, start, end):
cameras_list, lenses_list, focals_list, apertures_list, exposures_list = [], [], [], [], [] cameras_list, lenses_list, focals_list, apertures_list, exposures_list = [], [], [], [], []
isos_list, dimensions_list, cameras_lenses_list, dates_list = [], [], [], [] isos_list, dimensions_list, cameras_lenses_list, dates_list = [], [], [], []
for data in exif_dict_list: for data in exif_dict_list:
if start <= data['DateTimeOriginal'] <= end: if data['DateTime'] and start <= data['DateTime'] <= end:
cameras_list.append(data['Model']) cameras_list.append(data['Model'])
lenses_list.append(data['LensModel']) lenses_list.append(data['LensModel'])
focals_list.append(data['FocalLength']) focals_list.append(data['FocalLength'])
@ -88,7 +145,7 @@ def extractor(input_files, start, end):
if data['LensModel']: if data['LensModel']:
cameras_lenses_list.append( cameras_lenses_list.append(
"{} + {}".format(data['Model'], data['LensModel'])) "{} + {}".format(data['Model'], data['LensModel']))
dates_list.append(data['DateTimeOriginal'].strftime("%Y%m%d")) dates_list.append(data['DateTime'].strftime("%Y%m%d"))
for camera in list(set(cameras_list)): for camera in list(set(cameras_list)):
if camera: if camera:
cameras.update({camera: cameras_list.count(camera)}) cameras.update({camera: cameras_list.count(camera)})
@ -139,7 +196,15 @@ if __name__ == '__main__':
"infile", help="input file (EXV, JPG, CR2, ...). Can be more than one.", nargs='+') "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("-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("-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() 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: 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. 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: if not args.end_date:
@ -150,4 +215,5 @@ if __name__ == '__main__':
except ValueError: except ValueError:
print("Date must be YYYYMMDD.") print("Date must be YYYYMMDD.")
exit() exit()
print(json.dumps(extractor(args.infile, datetime.strptime(args.start_date, "%Y%m%d"), datetime.strptime(args.end_date, "%Y%m%d")), indent=4, sort_keys=True)) 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))