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.

Revisions

  1. virus-warnning created this gist Sep 4, 2019.
    112 changes: 112 additions & 0 deletions classify.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,112 @@
    """
    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()