How to organize/sort images by EXIF image data

I have recovered images from a lost partition and I need to sort them or put them into a folder by the creation date in the EXIF data of each image.

I have installed digiKam and also shotwell but have not found the way to do this on any of the options.

Can anyone explain to me how to do it with either of those programs or by any other method?


Solution 1:

My favorite solution is to set the file date the same as the exif photo date. Doing this, you can sort the files using any file explorer tool.

  1. Install jhead (apt-get install jhead)
  2. Go to the photos directory and run this command jhead -ft *. This will set the file date in the filesystem with the create date of the exif metadata
  3. Now just go to the top menu (in Ubuntu you most go with the mouse through the top of the monitor screen), select View → Sort Images → By Date.

Solution 2:

I recommend using exiftool. You can install it with

sudo apt install exiftool

Here's a sample command that renames the files based on creation date in YYYYMMDD format and appends a sequence number at the end.

exiftool '-filename<CreateDate' -d %Y%m%d%%-.4nc.%%le -r

And here's a sample command that moves image.jpg into a directory with its creation date as its name, in the 'YYYY-MM-DD' format.

exiftool -d %Y-%m-%d "-directory<datetimeoriginal" image.jpg

There's more sample commands in the documentation: https://sno.phy.queensu.ca/~phil/exiftool/filename.html

Solution 3:

This is the code I am using. It renames the photos adding YYYYMMDD_originalname.jpg

#! /bin/bash
shopt -s globstar || exit
for PIC in **
do
# look only for jpg
if [[ "$PIC" =~ \.JPG$ ]] || [[ "$PIC" =~ \.jpg$ ]]; then
    # ignore jpg that have 8 numbers at beginning followed by _ or after IMG_ or P_ and followed by _ (already date stamped)
    if [[ "$PIC" =~ [[:digit:]]{8}_.*$ ]] || [[ "$PIC" =~ IMG_[[:digit:]]{8}_.*$] ]] || [[ "$PIC" =~ P_[[:digit:]]{8}_.*$] ]]; then
    :
    else
        # get the date and time from the tag
        DATE=$(exiftool -p '$DateTimeOriginal' "$PIC" | sed 's/[: ]//g')
        echo "file_$PIC"
        # customize date, in this case eliminate the time, getting only the date in 8 numbers and adding _
        DATEMOD2=$(echo $DATE | sed -e 's/^\(.\{8\}\).*/\1_/')
        echo "datemod2_$DATEMOD2"
            # check if DateTimeOriginal was present
            if [[ "$PIC" == "$DATEMOD2$PIC" ]];then
            # as DateTimeOriginal is not present try with HistoryWhen
            DATE=$(exiftool -p '$HistoryWhen' "$PIC" | sed 's/[: ]//g')
            DATEMOD2B=$(echo $DATE | sed -e 's/^\(.\{8\}\).*/\1_/')
            echo "datemod2B_$DATEMOD2B"
                # check if HistoryWhen is present
                if [[ "$PIC" == "$DATEMOD2B$PIC" ]];then
                # nor the tag DateTimeOriginal, nor HistoryWhen present
                echo "skip"
                else
                # this will be done
                echo "mv -i "$PIC" $(dirname "$PIC")/"$DATEMOD2B""$PIC""
                #uncomment if you like it
                #mv -i "$PIC" $(dirname "$PIC")/"$DATEMOD2B""$PIC"
                fi
            else
            # this will be done
            echo "mv -i "$PIC" $(dirname "$PIC")/"$DATEMOD2""$PIC""
            #uncomment if you like it
            #mv -i "$PIC" $(dirname "$PIC")/"$DATEMOD2""$PIC"
            fi
     fi
fi
done

EDIT. In this modification, the date in the tag is passed to the name and also to the date attribute with touch. Also, if those tags do not exist, the date of modification tag is passed to the name of the file.

#! /bin/bash
shopt -s globstar || exit
for PIC in **
do
# look only for jpg
if [[ "$PIC" =~ \.JPG$ ]] || [[ "$PIC" =~ \.jpg$ ]]; then
        echo "file_$PIC"
        # get the date and time from the tag DateTimeOriginal
        DATE=$(exiftool -p '$DateTimeOriginal' "$PIC" | sed 's/[: ]//g')
        LONGDATE=$(echo $DATE | sed -e 's/^\(.\{12\}\).*/\1/')
            # check if DateTimeOriginal is 0000... OR empty
            if [[ "$LONGDATE" != "000000000000" ]] && [[ -n "$LONGDATE" ]]; then
            echo "datetimeoriginal_$LONGDATE"
            # modify the attribute date with the info in the tag date
            touch -t $LONGDATE "$PIC"
            # customize date, in this case eliminate the time, getting only the date in 8 numbers and adding _
            DATEMOD2=$(echo $DATE | sed -e 's/^\(.\{8\}\).*/\1_/')
            echo "datemod2_$DATEMOD2"
                    # skip renaming if
                    # 8 numbers at beginning followed by _ or after IMG_ or P_ and followed by _ (already date stamped)
                    if [[ "$PIC" =~ [[:digit:]]{8}_.*$ ]] || [[ "$PIC" =~ IMG_[[:digit:]]{8}_.*$] ]] || [[ "$PIC" =~ P_[[:digit:]]{8}_.*$] ]]; then
                    :
                    else
                    # this will be done

                    filename=$(basename "$PIC")
                    echo "$filename"
                    echo "mv -i \""$PIC"\" \""$(dirname "$PIC")"/"$DATEMOD2""$filename"\""
                    #uncomment if you like it
                    mv -i "$PIC" "$(dirname "$PIC")/$DATEMOD2$filename"

                    fi
            else
            # get the date and time from the tag HistoryWhen

            DATE=$(exiftool -p '$HistoryWhen' "$PIC" | sed 's/[: ]//g')
            LONGDATE=$(echo $DATE | sed -e 's/^\(.\{12\}\).*/\1/')

            # check if Historywhen is 0000... or empty
                if [[ "$LONGDATE" != "000000000000" ]] && [[ -n "$LONGDATE" ]]; then
                echo "historywhentag_$LONGDATE"

                touch -t $LONGDATE "$PIC"
                DATEMOD2B=$(echo $DATE | sed -e 's/^\(.\{8\}\).*/\1_/')
                echo "datemod2B_$DATEMOD2B"

                    if [[ "$PIC" =~ [[:digit:]]{8}_.*$ ]] || [[ "$PIC" =~ IMG_[[:digit:]]{8}_.*$] ]] || [[ "$PIC" =~ P_[[:digit:]]{8}_.*$] ]]; then
                    :
                    else
                    # this will be done             
                    filename=$(basename "$PIC")
                    echo "$filename"
                    echo "mv -i \""$PIC"\" \""$(dirname "$PIC")"/"$DATEMOD2B""$filename"\""
                    #uncomment if you like it
                    mv -i "$PIC" "$(dirname "$PIC")/$DATEMOD2B$filename"
                    fi

                else
                    # get the date and time from the tag tag filemodifydate

                    DATE=$(exiftool -p '$filemodifydate' "$PIC" | sed 's/[: ]//g')
                    LONGDATE=$(echo $DATE | sed -e 's/^\(.\{12\}\).*/\1/')

                    # check if filemodifydate is 0000... or  empty
                    if [[ "$LONGDATE" != "000000000000" ]] && [[ -n "$LONGDATE" ]]; then
                    #echo "filemodifydatetag_$LONGDATE"

                    #touch -t $LONGDATE "$PIC"
                    DATEMOD2C=$(echo $DATE | sed -e 's/^\(.\{8\}\).*/\1_/')
                    echo "datemod2C_$DATEMOD2C"

                        if [[ "$PIC" =~ [[:digit:]]{8}_.*$ ]] || [[ "$PIC" =~ IMG_[[:digit:]]{8}_.*$] ]] || [[ "$PIC" =~ P_[[:digit:]]{8}_.*$] ]]; then
                        :
                        else
                        # this will be done             
                        filename=$(basename "$PIC")
                        echo "$filename"
                        echo "mv -i \""$PIC"\" \""$(dirname "$PIC")"/"$DATEMOD2C""$filename"\""
                        #uncomment if you like it
                        mv -i "$PIC" "$(dirname "$PIC")/$DATEMOD2C$filename"
                        fi

                    else

                    echo "Error, NO date available"
                    fi
                fi
            fi
fi
done

For sorting in folders (year and month) (YYYYMM):

exiftool "-Directory<DateTimeOriginal" -d "%Y%m" *