Capítulo 3 Metadatos

Algunos ficheros de imagen contienen metadatos.

Los metadatos aportan información adicional sobre la imagen, como la calidad, contenido, coordenadas GPS, etc. Los metadatos se almacenan en grupos, cada uno de los cuales se conoce como modelo (model).

Aunque normalmente pasan desapercibidos y pocas aplicaciones los usan, pueden aportar mucha información útil. Sin embargo, esa información a veces puede ser sensible.

Por ejemplo, las fotografías tomadas con teléfono móvil contienen información sobre geolocalización, fecha y hora en que fueron tomadas, así como el modelo de teléfono, filtros usados, etc.

Generalmente los metadatos se eliminan cuando el fichero se edita de alguna forma y existen aplicaciones para visualizarlos, como Photo ME.

No todos los ficheros de imagen contienen metadatos, ni todos los que contienen metadatos tienen todos los modelos de metadatos que existen. Algunos modelos sólo están disponibles para ciertos tipos de fichero, por ejemplo, los de animación sólo pueden estar presentes en ficheros GIF.

Modelos

Se puede consultar qué modelos existen y cuales soporta cada tipo de fichero en la tabla 13.

Veamos cada uno de ellos con más detalle.

Comentarios

El modelo FIMD_COMMENTS contiene etiquetas de comentarios. Estas etiquetas permiten añadir información general a ficheros de imagen.

Modelos EXIF

Las etiquetas EXIF (Formato de archivo de imagen intercambiable) contienen información técnica sobre la imagen que las cámaras y teléfonos móviles añaden a cada imagen. Estos datos pueden incluir información sobre la marca y modelo de la cámara, tiempos de exposición, información sobre la óptica, coordenadas GPS, etc.

FreeImage agrupa las etiquetas EXIF en seis modelos separados: FIMD_EXIF_MAIN, FIMD_EXIF_EXIF, FIMD_EXIF_GPS, FIMD_EXIF_MAKERNOTE, FIMD_EXIF_INTEROP y FIMD_EXIF_RAW. Este último modelo contiene las etiquetas EXIF en crudo, es decir, sin analizar.

Para ver una referencia completa de las etiquetas EXIF definidas se puede consultar exiftool.org.

Modelo IPTC

El modelo IPTC (Consejo Internacional de Telecomunicaciones de Prensa) está orientado a intercambiar información en el entorno del periodismo. Contiene datos sobre el autor, el editor, fechas de intercambio, urgencia, etc.

Para ver una referencia completa de las etiquetas IPTC definidas se puede consultar exiftool.org o iptc.org.

Modelo XMP

El modelo XMP, inicialmente propuesto por Adobe, puede contener datos muy variados, incluidos muchos de los definidos en EXIF o IPTC: datos de la cámara, perspectiva, autoría...

Se puede consultar una referencia en exiftool.org.

Modelo GeoTiff

Estos metadatos están diseñados para imágenes georeferenciadas, como imágenes de satélites u ortofotográfías. Contienen información sobre la situación geográfica de las imágenes.

Puedes consultar la documentación en exiftool.org. Y hay más información en earthdata.nasa.gov.

Modelo Animation

Este modelo sólo se usa en imágenes de tipo GIF, y contiene información sobre las dimensiones, paleta, velocidad de la animación, en qué cuadro empieza la animación, etc.

Modelo Custom

Finalmente, el modelo custom contiene las etiquetas definidas por algún usuario, fuera de cualquiera de los modelos anteriores.

Etiquetas

Se usa una estructura para almacenar la información correspondiente a cada metadato llamada etiqueta (tag). Pero ésta estructura no está asociada a una estructura en el API de FreeImage, sino que para acceder a cada elemento se usan funciones.

Los metadatos de cada modelo se almacenan como una lista. Para obtener los metadatos de un modelo determinado usaremos un bucle en el que se obtiene el primero, y teniendo uno, a continuación se puede recuperar el siguiente.

Esto se hace mediante un procedimiento que tal vez te resulte familiar si has tenido que leer el contenido de un directorio de archivos:

  1. Se obtiene un manipulador de la lista de metadatos como valor de retorno de la función FreeImage_FindFirstMetadata, indicando en el primer parámetro el modelo, en el segundo un puntero a la estructura DIB que estamos procesando. En el tercer parámetro obtendremos un puntero al primer metadato.
  2. Si el manipulador obtenido es NULL significa que no existen metadatos de ese modelo, pero si es distinto de NULL podemos obtener el siguiente metadato de la lista mediante la función FreeImage_FindNextMetadata, indicando en el primer parámetro el manipulador obtenido previamente, y nos devolverá el puntero al siguiente metadato en el segundo. El valor de retorno será verdadero si se ha recuperado un metadato, o falso si no había más en la lista.
  3. Podemos repetir el paso anterior mientras el valor de retorno sea verdadero.
  4. Una vez terminada la lectura de metadatos de un modelo debemos liberar los recursos asociados al manipulador usando la función FreeImage_FindCloseMetadata.

Por ejemplo:

    FIMETADATA *handle;
    FITAG *tag;

    handle = FreeImage_FindFirstMetadata(model, dib, &tag);
    if(NULL != handle) {
        do {
           /* Procesar el metadato */
        } while(FreeImage_FindNextMetadata(handle, &tag));

        FreeImage_FindCloseMetadata(handle);
    }

Elementos de etiquetas

Los elementos de esta estructura se pueden ver en la tabla 11.

Para cada metadato tenemos una clave, key, que es un valor entero sin signo, y que podemos recuperar con la función FreeImage_GetTagKey.

Muchos metadatos tienen asociada una descripción, que indica el tipo de información que contienen. Para recuperarla se usa la función FreeImage_GetTagDescription. Si existe, el valor de retorno será una cadena de texto terminada en cero, en caso contrario será NULL.

Existen varios tipos para almacenar valores de etiquetas de metadatos, como se muestra en la tabla 12, para recuperar el tipo de una etiqueta se usa la función FreeImage_GetTagType.

Cada etiqueta puede almacenar varios datos del tipo anterior, almacenados de forma consecutiva en forma de array. Para recuperar el número de datos que contiene una etiqueta usaremos la función FreeImage_GetTagCount.

El tamaño total de los datos en bytes, corresponde al producto de tamaño del tipo por el número de datos, también se puede obtener mediante la función FreeImage_GetTagLength.

Finalmente, para obtener los valores usaremos la función FreeImage_GetTagValue, que nos devolverá un puntero void a los datos en bruto.

Por ejemplo, la latitud GPS está contenida en dos etiquetas. La primera tiene la clave 'GPSLatitude', su descripción es 'Latitude' y su ID es 2.

El tipo de datos es el 5, FIDT_RATIONAL. Este tipo de dato consiste en dos enteros de 32 bits, que se usan para formar una fracción, el primer entero es el numerador y el segundo el denominador.

Esta etiqueta contiene tres valores de este tipo, correspondientes a grados, minutos y segundos, y ocupa en total 24 bytes.

Así, si los valores son 48, 1, 30, 1, 238428. 10000, el valor de la latitud será 48º 30' 23.8428".

La segunda etiqueta tiene la clave 'GPSLtitudeRef', su descripción es 'North or South Latitude' y su ID es 1.

El tipo de datos es una cadena de caracteres ASCII terminada en cero.

Esta etiqueta contiene dos valores de tipo ASCII y ocupa dos bytes.

Los valores pueden ser "N" o "S", para indicar si se trata de latitud Norte o Sur, respectivamente.

Sin embargo no necesitamos preocuparnos demasiado por procesar los valores de cada metadato, ya que FreeImage también nos proporciona una función, FreeImage_TagToString que convierte ese valor en una cadena de caracteres. Esto es muy útil en ciertos casos en los que el valor numérico enumera ciertos valores en un dominio concreto. Por ejemplo, el valor 1 de la clave 'ColorSpace' del modelo 'EXIF', indica 'sRGB', o el valor 1, 2, 3, 0 de la clave 'ComponentsConfiguration', indica 'YCbCr', o el valor 0 de la clave 'ExposureMode' indica 'Auto exposure', etc.

Otras funciones

Disponemos de algunas funciones adicionales útiles a la hora de leer metadatos. FreeImage_GetMetadataCount nos devuelve el número de etiquetas que contiene un modelo determinado.

Y la función FreeImage_CloneMetadata copia los metadatos de un FIBITMAP a otro.

Ejemplo 3

Nombre Fichero Fecha Tamaño Contador Descarga
Ejemplo 3 FreeImage003.zip 2023-04-26 1865 bytes 201

Crear metadatos

Podemos añadir metadatos a los ficheros de imagen. Para ello comenzaremos creando una nueva etiqueta, ya sea vacía, usando la función FreeImage_CreateTag o bien creando una copia de una etiqueta existente, mediante la función FreeImage_CloneTag.

Para establecer los valores de la etiqueta disponemos de un juego de funciones para cada elemento.

FreeImage_SetTagKey asigna la clave. Es obligatorio hacerlo, y además debe ser un valor de clave único dentro de cada modelo. Si se asigna con éxito retorna TRUE.

FreeImage_SetTagDescription asigna la descripción. Esta asignación es opcional, y dado que no se almacena en el fichero, no será necesario hacerlo. Si se asigna con éxito retorna TRUE.

FreeImage_SetTagID asigna el ID. Esta asignación también es opcional. Si se asigna con éxito retorna TRUE.

FreeImage_SetTagType asigna el tipo de dato de la etiqueta. Esta asignación es obligatoria. Si se asigna con éxito retorna TRUE.

FreeImage_SetTagCount asigna el número de datos en la etiqueta. Esta asignación también es obligatoria. Si se asigna con éxito retorna TRUE.

FreeImage_SetTagLength asigna el tamaño de los datos de la etiqueta. Esta asignación es obligatoria. Si se asigna con éxito retorna TRUE.

FreeImage_SetTagValue asigna el valor de los datos de la etiqueta. Esta asignación es, por supuesto, obligatoria, y siempre debe hacerse después de haber asignado el número y el tamaño de los datos. Si se asigna con éxito retorna TRUE.

Una vez creada la etiqueta podemos añadirla a un fichero de imagen usando la función FreeImage_SetMetadata, indicando el modelo en el que queremos insertarla, el FIBITMAP, la clave y la etiqueta.

Si se indica un valor NULL para la etiqueta, se eliminará el metadato. Si además el valor de la clave también es NULL, se eliminará el modelo completo.

Para finalizar, hay que liberar la etiqueta usando la función FreeImage_DeleteTag.

También disponemos de una función FreeImage_SetMetadataKeyValue que crea, asigna y añade la etiqueta en una única operación, siempre que el tipo de dato sea FIDT_ASCII, es decir, texto. Esta función require cuatro parámetros: el modelo, el FIBITMAP, la clave y la cadena de texto terminada en cero del valor.

Por ejemplo:

    FITAG *tag;
    unsigned short int fecha[] = {2023, 5, 22};
...
		// Insertar un comentario:
		FreeImage_SetMetadataKeyValue(FIMD_COMMENTS, dib, "Autor", "Fulanito de tal");
		// Insertar un comentario mediante una etiqueta:
		tag = FreeImage_CreateTag();
		FreeImage_SetTagKey(tag, "Fecha"); // Opcional
		FreeImage_SetTagType(tag, FIDT_SHORT);
		FreeImage_SetTagCount(tag, 3);
		FreeImage_SetTagLength(tag, 3*sizeof(unsigned short int));
		FreeImage_SetTagValue(tag, (const void*)fecha);
		FreeImage_SetMetadata(FIMD_COMMENTS, dib, "Fecha", tag);
                FreeImage_DeleteTag(tag);

Por supuesto, cualquier modificación que hagamos en los metadatos la estamos haciendo en memoria. Si queremos que los cambios sean permanentes tendremos que guardar el mapa de bits de nuevo en un fichero.

Ejemplo 4

Nombre Fichero Fecha Tamaño Contador Descarga
Ejemplo 4 FreeImage004.zip 2023-04-26 2151 bytes 202