pictogram Auteur : Patrick Biker
Date : 12 Nov 2005
Révision: 14 nov 2005

Au menu : WinDev, WLangage et photos numériques

Les informations EXIF mémorisées dans une photo numérique

WinDev 10 permettra d'accéder en WLangage aux données EXIF d'une image numérique. Cette nouvelle fonctionnalité est sans doute la conséquence d'une demande des utilisateurs dans ce domaine. Dans cette page, nous verrons comment accéder aux informations EXIF d'une image numérique, dans le but précis d'extraire la vignette (thumbnail en anglais).

Une photo numérique au format DCF (Digital Camera File), donc type JPEG, contient des informations sur la photo : Taille, dimension, marque et modèle de l'appareil photo, présence d'une vignette, etc. L'encodage de ces informations est réalisé au format EXIF, qui sera décrit plus loin.

Ce document s'intéresse à la programmation en WLangage de la lecture de la vignette.

Format DCF / Exif

Une photo numérique est d'abord un fichier binaire dont la structure est la suivante :

Récupération de la vignette

Pour accéder à la vignette, il faut lire la première structure de répertoire (0th IFD). Elle contient un pointeur vers la deuxième structure de répertoire (1st IFD). Cette structure contient une pointeur vers les données brutes de la vignette.

Code WLangage

Le code WLangage donné ici peut être testé dans une fenêtre WinDev très simple qui contiendra un bouton et un champ Image, nommé ImageVignette.

Tout ce qui suit concerne le code du bouton. Tout commence par l'ouverture du fichier.

IdFichier = fOuvre("cat.jpg", foLecture)
SI IdFichier = -1 ALORS RETOUR

Le code se terminera par la fermeture du fichier. C'est une bonne habitude d'écrire immédiatement ce traitement, pour éviter de l'oublier

fFerme(IdFichier)

Lecture de l'entête du fichier JPEG

Démarrons maintenant la lecture du fichier. Un fichier DCF valide débute par une entête de fichier JPEG de 12 octets de long. Nous utiliserons une chaine fixe en guise de buffer de lecture.

sBuffer est une chaîne fixe sur 12
SI 12 <> fLit(IdFichier, 12, &sBuffer) ALORS GOTO _Erreur

Pour garder un code simple, notre gestion d'erreur est minimale. Elle consiste à sauter (GOTO) vers l'instruction qui ferme le fichier. Une autre manière de procéder consiste par exemple à définir une "exception utilisateur".

Contrôle du format JPEG

Un minimum de vérification est à réaliser sur les données lues. Il s'agit de valider la structure du fichier JPEG.

Dans un fichier JPEG, les 4 premiers octet doivent être FFD8FFE1. Nous utilisons ici la notation hexadécimale. FFD8 est le marqueur SOI (Start Of Image). FFE1 est le marqueur APP1.

Nous allons donc construire une valeur de contrôle qui sera comparée au 4 premiers caractères du buffer lu. Le WLangage permet d'écrire des données en hexadécimal, ce qui nous évitera une conversion.

sControle est une chaîne fixe sur 4
sControle = Caract(0xFF)+Caract(0xD8)+Caract(0xFF)+Caract(0xE1)
SI sBuffer[[1 A 4]] <> sControle ALORS GOTO _Erreur

Nous contrôlons également que l'entête contient bien "Exif" en toutes lettres en position 7 dans le buffer.

SI Position(sBuffer, "Exif") <> 7 ALORS GOTO _Erreur

L'entête TIFF

Immédiatement après l'entête du fichier JPEG débute un nouveau bloc de données, l'entête TIFF composé de 8 octets. La position de cette entête TIFF servira de référence par la suite pour calculer d'autres positions dans le fichier. Nous utilisons le même buffer pour lire les données.

SI 8 <> fLit(IdFichier, 8, &sBuffer) ALORS GOTO _Erreur

Encodage Motorola ou Intel

Ce bloc nous renseigne sur le format de stockage des nombres dans le fichier DCF. Deux codages existent : Intel et Motorola. Dans le codage Intel, les octets composant un entier sont placés dans l'ordre poids faible - poids fort.

Par exemple, selon que notre fichier JPEG utilise le codage Intel ou Motorola, la valeur A1C6 (41414) sera décrite par deux octets consécutifs ayant pour valeurs :

Le codage Intel

Les ordinateurs de type PC utilise le codage Intel (même si le processeur est AMD). Il sera donc plus facile de traiter un fichier codé en Intel.

L'entête TIFF débute par les caractères "II" si le codage est Intel, et par "MM" si le codage est Motorola.

CodageMotorola est un booléen
CodageMotorola = (sBuffer[[1 A 2]] = "MM")

Juste après l'information de codage, on trouve 2 octets avec la valeur constante 0x002A. Remarquons au passage que si le codage est Intel, on trouvera Ox2A00.

Structure de répertoire (IFD)

Les 4 octets suivant codent un décalage (offset en anglais) permettant de trouver la première structure de répertoire (IFD) du fichier. Rapellons-nous que cette structure contient elle-même un pointeur vers une autre structure (IFD) qui décrit la vignette.

Dans la majorité des cas, la structure de répertoire (IFD) vient immédiatement après l'entête TIFF. Le décalage, qui se mesure depuis le début de l'entête TIFF vaut donc 8 (l'entête TIFF est composée de 8 octets). Pour décoder ce décalage, il faudra procéder différemment selon que le codage est Intel ou Motorola.

Si le codage est Intel, il est possible d'affecter directement sBuffer[[5 à 8]] dans un entier, si le codage est Motorola, il faudra inverser tous les octets. Nous utiliserons pour cela la fonction ConversionMotorola qui recoit un entier sur 2 ou 4 octets et renvoie le même entier après avoir inversé tous ces octets. Le code de cette fonction est donné plus loin.

Pour affecter l'entier "decalage" on utilise la commande Transfer() qui réalise une copie de mémoire à mémoire.

decalage est un entier
SI CodageMotorola ALORS
    Transfer(&decalage, (&sBuffer + 4), 4)
    decalage = ConversionMotorola(decalage)
SINON       
    Transfer(&decalage, (&sBuffer + 4), 4)
FIN

Si le décalage vaut 8. Le bloc IFD suivant suit immédiatement. Dans le cas contraire, il faut se positionner dessus.

SI decalage > 8 ALORS
    fPositionne(IdFichier, 12 + decalage, fpDébut)
FIN

Lecture du 1er IFD

Chaque structure de répertoire débute par l'indication du nombre d'éléments. Cette valeur occupe 2 octets.

nbElement est un entier sur 2 octets
SI 2 <> fLit(IdFichier, 2, &nbElement) ALORS GOTO _Erreur
SI CodageMotorola ALORS nbElement = ConversionMotorola(nbElement)

Chaque élément du répertoire fait 12 octets. On va sauter ces élément pour aller directement au pointeur.

fPositionne(IdFichier, 12 * nbElement, fpCourant)

Le pointeur sur la vignette est une valeur sur 4 octets. C'est un décalage vers une nouvelle structure de répertoire (IFD) qui décrit la vignette.

SI 4 <> fLit(IdFichier, 4, &decalage) ALORS GOTO _Erreur
SI CodageMotorola ALORS decalage = ConversionMotorola(decalage)
SI decalage = 0 ALORS GOTO _Erreur
fPositionne(IdFichier, 12 + decalage, fpDébut)

Le 2ème IFD décrit la vignette

Cette structure débute aussi par l'indication du nombre d'éléments.

SI 2 <> fLit(IdFichier, 2, &nbElement) ALORS GOTO _Erreur
SI CodageMotorola ALORS nbElement = ConversionMotorola(nbElement)

Ici, il faut lire tous les éléments pour récupérer la taille, et la position de la vignette dans le fichier (décalage). Chaque élément a une longeur de 12 octets. Il est composé de 4 parties : Tag (2 octets), Format (2 octets), composant (4 octets), et enfin données (4 octets).

L'élément contenant la taille est désigné par le Tag 0x0202. L'élément contenant le décalage est désigné par le Tag 0x0201.

tailleVignette est un entier
BOUCLE (nbElement)
    SI 12 <> fLit(IdFichier, 12, &sBuffer) ALORS GOTO _Erreur
    SI sBuffer[[1 A 2]] = Caract(2)+Caract(2) ALORS
        Transfer(&tailleVignette, (&sBuffer + 8), 4)
        SI CodageMotorola ALORS tailleVignette = ConversionMotorola(tailleVignette)
    FIN
    SI CodageMotorola ALORS
        SI sBuffer[[1 A 2]] = Caract(2)+Caract(1) ALORS
            Transfer(&decalage, (&sBuffer + 8), 4)  
            decalage = ConversionMotorola(decalage)
        FIN
    SINON
        SI sBuffer[[1 A 2]] = Caract(1)+Caract(2) ALORS
            Transfer(&decalage, (&sBuffer + 8), 4)  
        FIN
    FIN
FIN

Extraction de la vignette

La présence d'une vignette n'est pas systématique. Si la vignette est absente, le décalage vaudra zéro. Dans le cas contraire, on va extraire la vignette et créer un fichier JPEG sur le disque.

SI decalage = 0 ALORS GOTO _Erreur
fPositionne(IdFichier, 12 + decalage, fpDébut)
sBufferVignette est une chaîne
sBufferVignette = Répète(Caract(0), tailleVignette)
SI tailleVignette <> fLit(IdFichier, tailleVignette, &sBufferVignette) ALORS GOTO _Erreur
fSauveTexte("vignette.jpg", sBufferVignette)

Affichage de la vignette

La vignette est maintenant sauvée sur le disque. Nous allons l'afficher dans un champ image.

ImageVignette = ""
ImageVignette..Largeur = ExtraitChaîne(InfoBitmap("vignette.jpg"),2)
ImageVignette..Hauteur = ExtraitChaîne(InfoBitmap("vignette.jpg"),3)
ImageVignette = "vignette.jpg"

C'est fini ! Il nous reste à ferme le fichier.

fFerme(IdFichier)
RETOUR

_Erreur:
Erreur("Il y a une erreur")
fFerme(IdFichier)

Codes complets à copier-coller

A la demande générale, un code source plus accessible pour le copier/coller.

Code du bouton

Source en WinDev 9 à télécharger

Fonction ConversionMotorola

Dans le cas d'un fichier au format Motorola, cette fonction inverse les octets afin de les remettre dans le format Intel.

PROCEDURE ConversionMotorola(paramEntier2ou4)
tbOctet est un tableau de 4 entiers sans signe sur 1 octet
en2 est un entier sans signe sur 2 octet
en4 est un entier sans signe sur 4 octets
en1 est un entier sans signe sur 1 octet
// transfère l'entier dans le tableau
// les éléments du tableau sont inversés
// le tableau est retransféré dans l'entier
SI Dimension(paramEntier2ou4) = 4 ALORS
    en4 = paramEntier2ou4
    Transfer(&tbOctet, &en4, 4)
    en1 = tbOctet[1]; 
    tbOctet[1] = tbOctet[4]
    tbOctet[4] = en1
    en1 = tbOctet[2]
    tbOctet[2]=tbOctet[3]
    tbOctet[3]=en1  
    Transfer(&en4, &tbOctet, 4)
    RENVOYER en4
SINON
    en2 = paramEntier2ou4
    Transfer(&tbOctet, &en2, 2)
    en1 = tbOctet[1]; 
    tbOctet[1] = tbOctet[2]
    tbOctet[2] = en1
    Transfer(&en2, &tbOctet, 2)
    RENVOYER en2
FIN

Glossaire

Les formats EXIF et DCF sur le net

Ce site en anglais permet d'aborder le format EXIF de manière très pratique avec des exemples. Il contient également des liens vers d'autres sites de référence.

Exif file format

Description en anglais très complète de tous les marqueurs (Tag) utilisés dans le format EXIF.

Exif tags

Document PDF décrivant le format Exif 2.2

Design rule for Camera File system (DCF)

Exchangeable image file format for Digital Still Camera : Exif