Régulièrement, c'est à dire pratiquement à chaque fois que j'annonce la mise en ligne d'une nouvelle galerie de photos, on me demande comment je fais pour récupérer le nom de l'objectif. Si le fait de l'extraire des données EXIF semble une évidence, on découvre rapidement que ce n'est pas accessible via un tag standard. Et donc que le support EXIF de PHP ne permet pas de le récupérer simplement.
Et pour cause. Le type d'objectif utilisé est en fait une donnée ajoutée, le cas échéant, par le constructeur du boîtier dans un tag appelé MakerNote. Il s'agit d'un blob au format propriétaire à chaque constructeur qui y met ce que bon lui semble. Aussi, pour le parser efficacement, il faut soit avoir recours à une bibliothèque PHP additionnelle, soit se palucher le MakerNote à la main. Ayant déjà assez de code PHP qui tourne comme ça, j'ai choisi la deuxième solution...
Il faut donc récupérer le tag MakerNote et le parcourir à la recherche des données qui nous intéressent. Pour l'extraire, rien de plus simple, on fait appel au support EXIF de PHP :
$exif = exif_read_data("$file", 0, true);
On récupère en sortie un tableau contenant toutes les données EXIF dans $exif, par section. Le MakerNote se trouve alors dans :
$exif['EXIF']['MakerNote']
Dans la mesure où le format du MakerNote est dépendant du constructeur, il est toujours bon de vérifier le nom de ce dernier. On le trouvera dans :
$exif['IFD0']['Make']
J'utilise un boîtier Pentax depuis pas mal de temps, j'attends donc la valeur PENTAX. On peut également tester les premiers octets du MakerNote pour vérifier qu'il correspond bien à l'entête du constructeur.
On peut maintenant s'attaquer au MakerNote Pentax proprement dit. Heureusement, son le format est plutôt bien documenté. Le tag qui nous intéresse est appelé LensType. Il porte le code 0x003f et désigne, selon les modèles de boîtiers, un mot de deux ou trois octets utilisé pour désigner le type d'objectif.
On pourrait s'attendre à devoir chercher 0x003f et extraire les trois octets qui suivent. Ce n'est malheureusement pas aussi simple. J'ai découvert empiriquement que suivaient deux octets de valeur 0x0001, puis quatre autres qui désignent je pense la taille du mot stockant la valeur recherchée. La valeur de ce mot change selon les boîtiers[1] ; elle vaut en effet 0x00000003 sur mon K20D et 0x00000002 sur le K100D. Suivent enfin les deux octets qu'on cherche. Si je ne doute pas que certains en auront les yeux qui piquent, voici comment j'extrais cette valeur :
$lenstype = bin2hex(substr(strstr($exif['EXIF']['MakerNote'], "\x00\x3f\x00\x01\x00\x00\x00"), 8, 2));
Ceci nous retourne la valeur hexadécimale sous forme de chaîne. Chaîne qu'il ne reste plus qu'à associer à l'objectif correspondant, conformément aux valeurs ce que nous donnent les spécifications. Dans la pratique, ces dernières sont exactes pour les objectifs fabriqués par Pentax. Par contre, dès qu'on passe à du Sigma ou du Tamron, les références se font plus aléatoires. Aussi je vous conseille d'aller chercher la valeur par vous même.
Pour l'affichage du nom, on utilisera un tableau associant le code à une chaîne décrivant l'objectif :
$lensarray = array( "0100" => "PENTAX K or M Lens", "0200" => "PENTAX A Lens", "032e" => "Sigma 100-300 F4 APO DG EX", "040c" => "smc PENTAX-FA 50mm F1.4", // etc. ) echo $lensarray[$lenstype];
Si on utilise des objectifs démunis d'électronique, comme les antiquités que j'utilise parfois ou mon tout récent fish-eye, le boîtier est bien incapable de déterminer leur type. Cependant, en calibrant la fonction Shake Reduction à l'allumage, on lui fournit la focale de l'objectif utilisé. Ceci permet de discriminer plusieurs objectifs qui retourneraient le même code, 0x0100 ou 0x0200 par exemple, s'ils ont des focales différentes. Pour mémoire, on récupère la focale ainsi :
eval("return $exif['EXIF']['FocalLength'];");[2]
Enfin, j'ai remarqué que le MakerNote que je récupérais suite à un import EXV par exiv2 me donnait un format légèrement différent. Dans ce cas, si j'ai bien un 0x003f0001, ce dernier est alors directement suivi de 0x0002 ou 0x0003 selon le boîtier, puis des deux octets nuls qui auraient du se trouver avant, et enfin le code de l'objectif sur deux octets mais précédé d'un nouvel octet nul. Il faut donc raccourcir la chaîne de comparaison et aller chercher la valeur un octet plus loin, c'est à dire ainsi :
$lenstype = bin2hex(substr(strstr($exif['EXIF']['MakerNote'], "\x00\x3f\x00\x01\x00"), 9, 2));
Pour ceux qui se demande pourquoi j'utilise l'export/import EXV, il s'agit d'effectuer une sauvegarde des données EXIF lorsque j'utilise un logiciel dont je sais qu'il me les amputera en sortie, pour les réinjecter ensuite. La retouche sous Gimp en est un exemple qui a tendance se tasser. L'autre cas est la génération de panoramas avec Hugin, lequel vire l'essentiel des données EXIF.
Tout ce qui précède ne vaut évidemment que pour les boîtiers Pentax. Je ne doute pas qu'une technique similaire puisse être appliquée à d'autres marques, mais il faudra pour cela aller chercher les valeurs qui vont bien dans les spécifications de leur MakerNote...
Notes
[1] Je vous encourage donc à vérifier les valeurs si vous possédez une autre boîtier...
[2] La valeur de focale étant retournée comme un nombre rationnel, l'appel à eval/return permet d'en sortir un entier.