Saturday, 13 February 2010

Fixing picture's date

spanish version - all english posts

I have a couple of bash scripts that I created when I bought my first digital camera back in 2003 or 2004. Those scripts allow me to clasify photos by date. The scripts use the date from the exif metadata that is embedded on each photo file header. That metadata is extracted using the strings command. With that timestamp the script renames each photo as YYYYMMDD_HHMMSS_#.jpg (the # indicates the number of the picture if several of them have the same timestamp). Also the scripts saves each photo in a folder tree of the form FOTOS/YYYY/MM.

With this system I have all the digital photos in a chronological order regardless the camera used to take them.

However, today, running the script to import photos from two of my digital cameras I've noticed that the pictures from one of the cameras had wrong timestamps. The camera which had wrong timestamps its a camera with a faulty battery and it seems that I made a mistake setting the date after the last reset. I set 2009 instead of current 2010 year. Nearly 200 photos will be bad classified if I don't find a cure for it.

Editing directly the ascii strings in the jpg header is useless, so a classical bash approach wont be useful... maybe some pythonic try with PIL... hmmm!

A quick search in Google shows a promising suggestion

jhead is a command line tool. If I had known it a few years ago I wouldn't have created the bash scripts for classifying photos. However I wouldn't have had the fun of creating them. jhead allows not only to display and manipulate exif's metadata but also allows to manipulate the photo files themselves.

Let me show you how to use jhead to fix the timestamp of the photos just in case you have the same problem I had. Of course, first you should install it (I use Ubuntu 9.10):

aptitude update && aptitude install jhead

Edit the picture files to change the date is as easy as running the following command. With this command I've managed to increase the timestamp in a year for 200+ photos in the SD card:

jhead -da2010:01:01-2009:01:01 *.jpg

jhead is well worth so I recommend that you take some time to read its manpage to see what it can offer (which is a lot).

Another example that I want to comment is how jhead manages to replace with in a single and simple command much of the functionality that implements the script that I use to sort my photos. Let's rename a picture in the form of YYYMMDD_HHMMSS.jpg using its metadata's timestamp:

~$ jhead -nf'%Y%m%d_%H%M%S' IMGP4890.JPG
IMGP4890.JPG --> 20100212_090624.jpg


As a tribute to my script (which has been working like a charm since 2004) I'm posting it below. Now that I know jhead my script will no longer be used... As you can see my old script in addition to rename the photos with their timestamp, as said before, the script moves them to a FOTOS/YYYY/MM directory tree structure, and it was able to manage photos with the same timestamp.

The script is a bit convoluted, reviewing it for writing this post I realize that my scripting technique has improved much over time, fortunately!:

#! /bin/bash
# vim:ts=4:sw=4:et:ft=sh
# $Source: camara_scripts/RCS/fotos_script_testTEST.sh,v $
# <hmontoliu@yahoo.es>
# 2004-01-13

echo "Utilizando directorio destino: $DESTDIR"
echo "OK (Ctrl-C aborta)?"; read foo

shopt -s extglob # for improved file pattern matching
for imgname in *.+(jpg|JPG);
    newname="$(head "$imgname"|strings|sed -n '/[0-9]\{4\}\:.*/ {s/ /_/g; s/://g; s/$/.jpg/; p}'|uniq)"
    eval $(echo $newname | sed -n 's/^\([0-9]\{4\}\)\([0-9]\{2\}\).*/year=\1 month=\2/p')
    mkdir -p ${DESTDIR}/$year/$month
    cp -v --backup=numbered --suffix=- "${imgname}" "${DESTDIR}/$year/$month/$newname"

# lets rename the xxx.jpg.~#~ generated by cp backup to xxx-#-.jpg
for file in $(find $DESTDIR -regex '.*~[0-9]+~')
    safenewname="$(echo $file | sed 's/\(.*\).jpg.~\(.[^~]*\)~/\1-\2.jpg/')"
    if [ -f "$safenewname" ]; then
        mv -iv "$file" "$safenewname"
        mv -v "$file" "$safenewname"

I will stop using it; A new script with jhead and rename (perl's prerename) should be no more than two lines!


Post a Comment