Hexinverter ¬ Sort Images by Exif Data

So, I love automation. I bristle at having to use GUIs for bulk operations, for example sorting and processing all of a batch of photos. Luckily, photos tend to get some metadata attached to them in the form of Exif data, and so along with copying the photos in bulk to a folder using whatever tool (either mounting the disk and just copying them, or using something like gphoto2), I have written a couple of scripts to bulk process them.

process-images

The first script is called process-images. This script used to extract thumbnails also from images, but now only renames them based on their timestamps in order to ensure that if I download DSC* files from multiple devices, they'll have unique names.

#!/bin/bash

process () {
    file=$1
    lowercase=$(echo "$file" | tr 'A-Z' 'a-z')
    mv $file $lowercase
    file=$lowercase
    timestamp=$(exiv2 pr -K Exif.Image.DateTime $lowercase  2>/dev/null | tr -s ' ' | cut -d' ' -f4- | tr -d ' :')

    if [ "$file"z != 'z' ]; then
    mv "$file" "$timestamp"-"$file"
    file="$timestamp"-"$file"
    fi
    echo "$1 -> $file"
}

for f in DSC_*.JPG DSC_*.NEF DSC_*.jpg DSC_*.tif; do
    process $f
done

sort-images

The next phase is to sort and contact sheet the images. This is a slightly more elaborate script which sorts the images by date, to a target folder named something like "YYYY/MM/DD/".

#!/bin/bash

ARCHIVE=/home/cas/Pictures/photos/

base () {
    b=$(basename $1 .nef)
    func_result="$b .nef"
    if [ $b == $1 ]; then
    b=$(basename $1 .jpg)
    func_result="$b .jpg"
    fi
    echo $func_result
}

# sort images by day
for f in *.jpg *.nef; do
    timestamp=$(exiv2 pr -K Exif.Image.DateTime $f  2>/dev/null | tr -s ' ' | cut -d' ' -f4 | tr ':' ' ')
    read year month day <<< "$timestamp"
    dest="$ARCHIVE/$year/$month/$day"
    outputs="$outputs $dest"
    mkdir -p "$dest"/edits
    read basefile ext <<< "$(base $f)"
    # We use -* as a way to provide edited versions w or w/o exif
    echo "($basefile$ext $basefile-*) -> $dest"
    if [ -e $basefile-* ]; then
    cp "$basefile-*" "$dest"
    fi
    cp "$basefile$ext" "$dest"
done

outputs=$(echo $outputs | tr ' ' '\n' | sort | uniq)

for d in $outputs; do
    # generate contact sheets for each day
    montage -verbose -label '%f' -font Helvetica -pointsize 10 -background '#333' -fill 'gray' -define jpeg:size=400x400 -geometry 400x400+2+2 -auto-orient "$d/*.jpg" "$d/zzzcontact.jpg"
done

And that's it, they are now archived along with anything I decided to edit at the time. This also creates an edits/ folder in which I generally prefer to put post-edited photos. A quick one liner like this, run from a top-level edits/ folder links them all to the same place for easy browsing (something similar could be done with contact sheets):

find -wholename '*edits/*.jpg' -exec ln -s (pwd)/'{}' edits/ \;

That's it! Happy photographing.