Compare commits
11 Commits
Author | SHA1 | Date | |
---|---|---|---|
17ce459630 | |||
ed525ccf2f | |||
6e702b3b3b | |||
664c8957fe | |||
f558b6574a | |||
62d759ab41 | |||
3fca8eb458 | |||
6962085961 | |||
1ea48e5af5 | |||
e91d51a43f | |||
47a2baf847 |
17
README
17
README
@ -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
38
README.md
Normal 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 qu’il sera possible de filtrer par objectif/boîtier, parce qu’au final, les stats de focale/ouvertures sont surtout pertinentes en isolant un seul objectif (dans le cas des zooms) ?
|
||||||
|
- En effet, l’option -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
84
exv.py
@ -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))
|
||||||
|
Reference in New Issue
Block a user