~
~
:wq

Sunday, 21 February 2010

Unicode and Python

spanish version - all english posts

This weekend I had in mind posting a few notes about how to correctly use unicode when programming with python. But after reviewing my bookmarks and rereading the magnificent post All About Python and Unicode I have come to the conclusion that publishing anything related to unicode would be light years behind of that wonderful tutorial.

I recommend anyone of you interested in the use of unicode that take a cup of coffee and read All About Python and Unicode.

Therefore, instead of the notes I was about to post, I will just list three references I personally use when I have to review unicode and python:

There are many more links, possibly better and newer, but with these three is sufficient to understand properly the use of unicode in python.

Unicode y python

english version - all spanish posts

Este fin de semana tenía en mente publicar unas notas sobre cómo usar correctamente unicode al programar con python, pero tras repasar los enlaces que tengo guardados al respecto y releer el magnífico post All About Python and Unicode he llegado a la conclusión de que cualquier cosa que publique quedaría a años luz de ese estupendo tutorial.

Recomiendo a cualquiera que tenga interés en el uso de unicode que coja una taza de café y le dedique un rato a All About Python and Unicode.

Por tanto, en lugar de las notas que había pensado poner, paso a listar tres enlaces que personalmente uso como referencia:

Hay muchos más enlaces, quizá mejores y más recientes, pero con estos tres es suficiente para entender correctamente el uso de unicode en python.

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

Lovely!

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

: ${DESTDIR:="$HOME/FOTOS/"}
echo "Utilizando directorio destino: $DESTDIR"
echo
echo "OK (Ctrl-C aborta)?"; read foo

shopt -s extglob # for improved file pattern matching
for imgname in *.+(jpg|JPG);
do
    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"
done

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

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

Rectificar la fecha en las fotografías

english version - all spanish posts

Tengo una serie de scripts en bash que creé cuando me compré la primera cámara digital, en el 2003 o 2004, que me permiten clasificar las fotos por fechas. Extraen los metadatos del encabezamiento de las fotografías mediante el comando strings. A partir de la fecha que proporcionan los metadatos exif renombran cada fotografía como AAAMMDD_HHMMSS_#.jpg (el # indica el número de fotografía en caso de que varias tuvieran el mismo timestamp) y además guardan cada fotografía en un directorio llamado FOTOS/AAA/MM. De esta forma tengo todas las fotografías digitales ordenadas por fechas independientemente de la cámara usada.

Resulta que hoy al ejecutar el script para importar las fotos de dos de mis cámaras digitales me he dado cuenta de que las fotos de una de las cámaras tenían mal puesta la fecha. Se trata de una cámara que tiene mal la batería y se resetea a la configuración de origen cada vez que la recargo. Por error al volver a conectar la cámara me equivoqué y puse año 2009 en lugar de 2010. Unas 200 fotos se clasificarán mal si no lo remedio.

Editar directamente el texto ascii del encabezamiento de las fotografías (el que se obtiene con strings) no sirve para cambiar la fecha (lo he probado) por lo que con las herramientas bash habituales no me iba a ser posible editar las 200 fotos con el año mal puesto.

Después de un rápido «googleo» obtengo una prometedora sugerencia:

jhead es una herramienta de línea de comandos que de haberla conocido hace unos años me hubiera ahorrado los scripts de bash pero también toda la diversión que tuve con ellos al hacerlos. Permite no solo mostrar y manipular los metadatos exif de las imágenes sino manipular los ficheros de las fotos a partir de estos datos.

Os muestro como usar jhead para reparar la fecha de las fotos por si os pasa algo similar. En primer lugar lo instalamos (yo uso en el ordenador donde guardo las fotos Ubuntu 9.10):

aptitude update && aptitude install jhead

Editar las fotos para cambiar la fecha es tan sencillo como ejecutar el siguiente comando que se encarga de modificar la fecha de las fotos añadiendo un año a todas ellas. Con este comando he conseguido incrementar la fecha en un año en las 200 fotos de la tarjeta:

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

jhead merece mucho la pena así que os recomiendo que os deis una vuelta por su manpage para ver todo lo que puede ofrecer (que es bastante).

Otro ejemplo que me gustaría comentaros es como jhead con un solo comando sustituye gran parte de la funcionalidad del script que uso para clasificar fotos. Vamos a renombrar una foto usando el timestamp de sus metadatos exif de modo que pase a llamarse AAAMMDD_HHMM.jpg:

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

Realmente sencillo.

Como homenaje a mi script que ha venido funcionando perfectamente desde el 2004 os lo muestro a continuación, ya que con jhead va a dejar de ser usado. Como podéis ver además de renombrar las fotos, como os he comentado al principio, el script las clasifica en directorios AAAA/MM y gestiona fotos con el mismo timestamp. El script es un poco enrevesado, revisándolo para este post me doy cuenta de que mi técnica de scripting afortunadamente ha mejorado mucho con el tiempo:

#! /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

: ${DESTDIR:="$HOME/FOTOS/"}
echo "Utilizando directorio destino: $DESTDIR"
echo
echo "OK (Ctrl-C aborta)?"; read foo

shopt -s extglob # for improved file pattern matching
for imgname in *.+(jpg|JPG);
do
    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"
done

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

En fin, voy a dejar de usarlo, con jhead y rename (prename de perl) el nuevo script se quedará en solo un par de líneas.