Le vieil homme et les JPEG

Plot

Un jour, au détour d'un darty, j'ai croisé un vieil homme entrain de chercher de l'aide auprès d'une gentille vendeuse parce que ses photos étaient cassées. La pauvre vendeuse savait vendre des ordinateurs, pas réparer des photos, j'ai donc proposé à l'homme de passer à Atilla, le lendemain soir vers 19h, où je devrais traîner, comme beaucoup de soirs pendant l'hiver.

Le lendemain soir, il a donc pénétré la salle, et c'est là que tout commence.

0xstuff

Les images étaient cassées. Sous windows vista sur son hp tout pourri, les images n'avaient plus qu'une icône. Nous avons donc prié vista de nous donner le poids des images, normal pour une image, par conséquent les données étaient toujours présentes.

Pour diagnostiquer, nous avons copié quelques images (seules les JPEG étaient affectées) sur ma debian.

Shotwell a été plus précis que l'afficheur d'images de windows, en nous précisant la source d'erreurs :

l'image ne commence pas par 0xffd8

Un coup de hexdump -n 64 -C image-001.jpeg nous a montré les premiers 64 octets de l'image sous cette forme :

00000000  00 d8 ff e0 00 10 4a 46  49 46 00 01 01 01 00 48  |......JFIF.....H|
00000010  00 48 00 00 ff db 00 43  00 06 04 04 04 05 04 06  |.H.....C........|
00000020  05 05 06 09 06 05 06 09  0b 08 06 06 08 0b 0c 0a  |................|
00000030  0a 0b 0a 0a 0c 10 0c 0c  0c 0c 0c 0c 10 0c 0e 0f  |................|
00000040

On reconnaît le header avec JFIF, (soit dit en passant, très pratique pour compter les images dans un gros fichier avec un coup de grep JFIF -c grosfichier.dat) et les premiers octets : 00 d8 ff e0.

Or, le format jpeg commence toujours par :

  • ff d8, fixe

  • ff ??, précise le type de l'image

Après avoir vérifié sur d'autres images avec un :

1
2
3
4
5
6
#!/bin/bash
for i in `ls *.jpeg`;
do
  echo $i;
  hexdump -n 16 -C $i;
done;

Nous (j'ai) supposé que le problème était uniquement le premier octet des images, passé de 0xFF à 0x00 (plein de 1 -> plein de 0). Symptôme surprenant, personne sur les Internets ne semble l'avoir rencontré.

Un petit script en python bien sale qui charge les images en mémoire en entier, change le premier octet et les écrit sur le même fichier a pris une petite heure pour les 7000 photos (je crois), éxécuté sur son hp sur une live ubuntu.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#!/usr/bin/python2.7
# -*- coding: utf-8 -*-

import os
import sys

def main():
  # lecture du fichier contenant la liste des fichiers, obtenue en faisant un "find . -name *.jpeg"
  with open(sys.argv[1],'r') as ofi:
    liste_fichiers = ofi.readlines()
    ofi.close()

  # pour chaque numéro de la liste des images
  for index in xrange(len(liste_fichiers)):

    # on retrouve le nom de l'image
    image = liste_fichiers[index]

    # affichage de la progression, histoire qu'on aille faire les courses en attendant
    print index,"/",len(liste_fichiers),image

    # lecture de l'image
    with open(image,'r') as ofi:
      data = ofi.read()
      ofi.close()

    # remplacement du premier octet
    data[0] = chr(0xff)

    # écriture de l'image (d'où le "w" au lieu du "r")
    with open(image,'w') as ofi:
      ofi.write(data)
      ofi.close()

if __name__ == "__main__":
  main()

Le vieil homme, ravi a insisté pour me payer, ce que j'ai refusé, bien entendu, alors il m'a invité à manger chez lui ou ce genre de trucs, je lui ai dit que si il voulait vraiment nous (atilla, mais en fait me) remercier, il pouvait ramener un gâteau le lendemain.

Épilogue

Le surlendemain, vers 19h30, PAF, pierre (il a des cheveux) surgit suivi par le monsieur et un gâteau aux amandes \o/ ! (Il a insisté pour me "parler" en privé et en a profité pour me glisser un billet, mais ne le dites à personne ;)

Conclusion

On aurait pu régler ça avec un coup de dd, ça aurait sûrement été plus vite à éxécuter, mais pas à coder, je n'étais pas trop à l'aise avec dd à l'époque.

blogroll

social