Monday, July 30, 2007

Sort Digital Photos

Over the years I have collected thousands of digital photos from birthday parties, events etc. Having only a 64MB in my Sony Memory Stick it gets filled up very fast even at 600x480 resolution which is the lowest setting. Its become a habit dumping the pictures onto a new folder on my hard drive every now and then. After trying to sort the mess manually to try to organize the photos chronologically I managed to lose the one most important piece of information, the datestamp when the image was originally taken. Apparently when you do a copy in bash it does not preserve the datestamp instead it overwrites it with the one at which time the file was copied. Had I used the -a switch with copy I wouldn't be in this mess.
Fortunately there was still hope. I discovered that all jpegs included header information which contains the original datestamp as well as some other things that I wasn't interested in.

This lead me to write a simple bash script to extract the datestamp and sort the pictures chronologically in my album.

Here is the pseudo code:
*accept minimum 2 arguments the path_to_images path_to_write_location recursive_flag
*parse the date string from the jpeg
*loop thru all the images
*if write location does not exist create it
*construct a directory structure to place the images in the format Year/Month
*if format directory structure does not exists create it
*otherwise check if the file name exists in that directory so that we dont overwrite it
*if it does rename it before we copy it to the destination folder

Here is the entire code:

#!/bin/bash
#filename: chronJpg.sh
#Date: Feb 17 2007
#Author: Dejan Tolj
#License: GPL
#
#Bash script to arrange jpeg images chronologically
#
#Usefull when original picture taken date has not been preserved by
#accidentally coping without the -a option.

which identify &>/dev/null
if [ $? != "0" ];
then
echo -e "identify not found, please install ImageMagick. http://imagemagick.com";
exit 0
fi

restoreDateStamp(){
# echo "Date stamp =$1"
# echo $1 | tr -d [:space:][:punct:] |touch -t /{} $2
date=`echo $1|cut -d: -f 1-4|tr -d [:space:][:punct:]` #2005:05:18 13:50:66

touch -t $date $2
# echo "Touch $date ->$2"
}

Month[1]="Jan"
Month[2]="Feb"
Month[3]="Mar"
Month[4]="Apr"
Month[5]="May"
Month[6]="Jun"
Month[7]="Jul"
Month[8]="Aug"
Month[9]="Sep"
Month[10]="Oct"
Month[11]="Nov"
Month[12]="Dec"

#parameters: jpg_path, write_location, recursive copy(optional)
PARAMSIZE=2

if [ $# -lt "$PARAMSIZE" ];
then
echo "Enter the path to your jpeg files and the write location"
echo `basename $0` "[path] [write_location] -r"
else
#get first parameter
jpg_path=$1
# echo $jpg_path
#get second parameter
write_location=$2
#echo $write_location
#get third parameter if present else search in current folder only
if [ "$3" = "-r" ]; then
echo "$3"
recursive=""
else
recursive="-maxdepth 1"
echo "$recursive"
fi

#Loop thru all jpegs and find the original date
for i in $( find $jpg_path $recursive -iname \*.jpg );
do
original_date=`identify -verbose $i |grep "Date Time Original"|awk '{print $4, $5}'`
#echo "OD=$original_date"

#Get Original year
original_year=`echo $original_date|cut -d: -f1`
#echo "OY=$original_year"

#Get Original month
original_month=`echo $original_date|cut -d: -f2`
#optionaly change date stamp

if [ ! -d "$write_location" ];
then
mkdir "$write_location"
fi

mydir="$write_location/$original_year/${Month[original_month]}"
filename=`basename $i` #strip the name of the image


if [ ! -d $mydir ];
then
# echo "Original month =$original_month"

mkdir -p $mydir
cp $i $mydir
#Optionally change the date stamp
#restoreDateStamp "$original_date" "$mydir/$filename"
else
#check name
if [ -f "$mydir/$filename" ];
then
echo "Renaming duplicate file"
if [ -f "$mydir/$filename"_$count ];
then
count=`ls $mydir/$filename_* |tail -1|cut -d_ -f2|tr -d [:alpha:][:punct:]`
count=$[$count+1]
else
count=0
fi
#rename
cp $i "$mydir/$filename"_$count
else
cp $i $mydir
fi
#restoreDateStamp "$original_date" "$mydir/$filename"
fi

echo "$i -> $mydir";
done
fi


exit 0