Skip to content

Instantly share code, notes, and snippets.

@virus-warnning
Created September 4, 2019 06:39
Show Gist options
  • Save virus-warnning/152f41fdb8f08c47ad8210c6d25944e0 to your computer and use it in GitHub Desktop.
Save virus-warnning/152f41fdb8f08c47ad8210c6d25944e0 to your computer and use it in GitHub Desktop.
Classify photos by EXIF attributes, designed for my wife.
"""
Classify photos by EXIF attributes.
Before classified:
DCIM
├── 100ANDRO
│   ├── DSC_2828.JPG
│   └── DSC_2830.JPG
├── 99ANDRO
│   ├── DSC_2822.JPG
│   └── DSC_2827.JPG
├── Camera
│   ├── C1
│   │   ├── P_20180124_165745.jpg
│   │   └── P_20180124_165756.jpg
│   ├── C2
│   │   ├── P_20180124_172141.jpg
│   │   └── P_20180124_172720.jpg
│   └── P_20180124_165611.jpg
└── DSC_2833.JPG
After classified:
DCIM
├── 100ANDRO
├── 99ANDRO
├── Camera
│   ├── C1
│   └── C2
└── Classified
├── 2015-11
│   ├── 29-062447-Sony-D5503.jpg
│   ├── 29-063154-Sony-D5503.jpg
│   ├── 29-063503-Sony-D5503.jpg
│   ├── 29-063617-Sony-D5503.jpg
│   └── 29-063810-Sony-D5503.jpg
└── 2018-01
├── 24-165610-ASUS-Z00UD.jpg
├── 24-165745-ASUS-Z00UD.jpg
├── 24-165755-ASUS-Z00UD.jpg
├── 24-172141-ASUS-Z00UD.jpg
└── 24-172719-ASUS-Z00UD.jpg
"""
import piexif
import json
import re
import os
import shutil
import time
from datetime import datetime
class ExifReader:
def __init__(self, photo):
self.ifds = piexif.load(photo)
def read_attribute(self, tag, ifd='0th'):
if ifd in self.ifds:
if tag in self.ifds[ifd]:
if isinstance(self.ifds[ifd][tag], bytes):
return self.ifds[ifd][tag].decode('ascii')
else:
return self.ifds[ifd][tag]
return None
def rearrange(photo_src, base_dest = 'rearranged'):
# See: http://www.cipa.jp/std/documents/e/DC-008-2012_E.pdf p.79
exr = ExifReader(photo_src)
brand = exr.read_attribute(271)
model = exr.read_attribute(272)
mtime = exr.read_attribute(306)
m = re.match(r'(\d{4}):(\d{2}):(\d{2}) (\d{2}):(\d{2}):(\d{2})', mtime)
if m is None:
exit()
if model.lower().startswith(brand.lower()):
camera = model
else:
camera = (brand + '-' + model)
camera = camera.replace('_', '-')
photo_dir = '{}/{}-{}'.format(base_dest, m[1], m[2])
photo_dest = '{}/{}-{}{}{}-{}.jpg'.format(photo_dir, m[3], m[4], m[5], m[6], camera)
if not os.path.isdir(photo_dir):
os.makedirs(photo_dir)
shutil.copy2(photo_src, photo_dest)
ts_mtime = time.mktime(time.strptime(mtime, '%Y:%m:%d %H:%M:%S'))
st = os.stat(photo_dest)
os.chmod(photo_dest, 0o644)
os.utime(photo_dest, (ts_mtime, ts_mtime))
os.remove(photo_src)
def main():
source_dir = 'DCIM'
target_dir = 'DCIM/Classified'
for base, directories, files in os.walk(source_dir):
if base == target_dir:
continue
for f in files:
if re.match(r'.+\.jpg', f, re.I):
photo_src = '{}/{}'.format(base, f)
rearrange(photo_src, target_dir)
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment