Monday 23 February 2009

Automatically resize the 'family pics' collection for a photo frame

I have a C109D 9" Digital Photo frame, which has a 2GB SD flash card. I thought that would have been enough, but I hit the limit over the weekend. This spurred to to finally figure out how to bulk compress images, and annoyingly, not to just convert them but to convert them in a different place so the originals are available for printing.

Resizing several Gb of images is a tedious job, fortunately there are some nifty tools out there to help. At the core of the process is the imagemagick convert program, the rest is syntactic script-sugar.

I wrote two scripts to do this, the first makes the first set of root folders in the target directory and calls the second to do the heavy lifting of per-root folder processing.

A bug in the photo-frame code meant it hung if it encountered an empty directory (took a while to figure that out), so as a post-process, after the compression process has completed, script1 finds all folders with no files, and adds a default as a workaround.

SCRIPT 1 - create.sh
My top level folders were simply the year, eg 2001 etc.

#!/bin/bash
PICS_HOME=/srv/FamilyPics
COMPRESS_HOME=/srv/compressedPics
cd $COMPRESS_HOME
#rm -rf $COMPRESS_HOME/*
for aDir in `find $PICS_HOME -maxdepth 1 -name '200*' -type d`
do
BASEDIR=`basename $aDir`
#echo Making top level folder: $COMPRESS_HOME/$BASEDIR
mkdir -p $COMPRESS_HOME/$BASEDIR
cd $aDir
find . -iname "*.jpg" -exec $PICS_HOME/compressFile.sh $PICS_HOME $COMPRESS_HOME $aDir "{}" \;
done

#Now fixup empty dirs that break the digital photoframe
PLACEHOLDER="$COMPRESS_HOME/2002/someplace/placeholder.jpg"
DIRS=`find $COMPRESS_HOME -type d`
for aDir in $DIRS
do
FILE_COUNT=`find $aDir -maxdepth 1 -type f |wc -l`
if [ $FILE_COUNT -eq 0 ]; then
echo need to fix dir $aDir : $FILE_COUNT
cp $PLACEHOLDER $aDir/.
fi
done


SCRIPT 2 - compressFile.sh
#!/bin/bash
PICS_HOME="$1"
COMPRESS_HOME="$2"
aDir="$3"
BASEDIR=`basename "$aDir"`
aFile="$4"

#echo "picsHome=[$PICS_HOME] compressHome=[$COMPRESS_HOME] aDir=[$aDir] baseDir=[$BASEDIR] aFile=[$aFile]"

FILENAME=`basename "$aFile"`
RELATIVE_PATH=`echo $aFile | sed -e 's/$FILENAME//g' |sed -e 's/ /-/g' | sed -e 's/\.\///g'`
OUTPUT_PATH="$COMPRESS_HOME/$BASEDIR/$RELATIVE_PATH"
OUTPUT_DIR=`dirname $OUTPUT_PATH`
#echo Making Folder path for output: $OUTPUT_DIR
mkdir -p $OUTPUT_DIR
# echo Compressing $BASEDIR/$aFile to $OUTPUT_PATH
cd `dirname $OUTPUT_PATH`
RAW_FILENAME="`basename $OUTPUT_PATH`"

if [ ! -f $OUTPUT_PATH ]; then
if [ -s "$PICS_HOME/$BASEDIR/$aFile" ]; then

cp "$PICS_HOME/$BASEDIR/$aFile" $OUTPUT_PATH
chmod +w $OUTPUT_PATH

# dd if=$PICS_HOME/jpg.hdr of="$RAW_FILENAME" bs=2 count=1
# echo RAW filename=$RAW_FILENAME
# echo Current dir `pwd`
# echo Compressing $OUTPUT_PATH
convert $RAW_FILENAME -encoding jpg -resize 640x480 -quality 86 -strip $RAW_FILENAME.tmp
rm "./$RAW_FILENAME"
mv "./$RAW_FILENAME.tmp" "$RAW_FILENAME"
OLDSIZE=`ls -l "$PICS_HOME/$BASEDIR/$aFile" | gawk 'FS=" " { print $5 }'`
NEWSIZE=`ls -l "$RAW_FILENAME" | gawk 'FS=" " { print $5 }'`
echo Compressed $OUTPUT_PATH from $OLDSIZE to $NEWSIZE
else
echo "File is empty: $PICS_HOME/$BASEDIR/$aFile"
fi
else
OLDSIZE=`ls -l "$PICS_HOME/$BASEDIR/$aFile" | gawk 'FS=" " { print $5 }'`
NEWSIZE=`ls -l "$RAW_FILENAME" | gawk 'FS=" " { print $5 }'`
echo Already Compressed $OUTPUT_PATH from $OLDSIZE to $NEWSIZE
fi

The result of this compression is that my 2Gb cart is now only 10% full rather than 100% full, so in theory, should be good for another 90 years, we'll probably have HD photo frames by then so the topic may become moot!

I hope these scripts will be useful fodder to anyone needing to do a similar thing.

No comments: