GINF53C4 - Projet Indexation Multimédia
Réalisation d'un système d'indexation d'images fixes

Note : ce sujet et les données associées (à l'exception des images) sont accessibles en local sur le serveur de l'UFR : Si vous travaillez sur les machines de l'UFR, il n'est donc pas nécessaire (et pas recommandé) de recopier les données dans votre répertoire. Vous pouvez ouvrir directement les fichiers à partir de /u/q/quenotg/GINF53C4/PROJET... (à l'exception des images que vous pouvez récupérer dynamiquement à partir de leur URL avec le programme fourni et qu'il n'est donc pas nécessaire de recopier non plus).

1. But

Réaliser un système qui permet d'attribuer des étiquettes sémantiques à des images en fonction de leur contenu à partir d'une collection d'entraînement contenant des images annotées. Évaluer ce système en utilisant une collection de validation contenant d'autres images annotées. Le système utilisera plusieurs descripteurs de contenu, un classificateur (SVM) et une méthode de fusion. Le système devra donner un score de probabilité de présence pour chaque concept dans chaque image. Pour chaque concept, il devra afficher la liste ordonnée des images de la collection de test ayant la plus grande probabilité de contenir le concept (top N avec mesure de la précision à N). La mesure de performance globale est la MAP. Plusieurs combinaisons seront comparées.

2. Données fournies

Les images et les annotations que que allez utiliser sont issues du projet VOC 2010 sur la classification automatique d'images (pour information, vous n'avez pas besoin d'étudier ce site).

Les données sont décomposées en deux parties : entraînement (train, 4998 images) et validation (val, 5105 images). Les répertoires train et val contiennent:

La liste des 20 concepts annotés se trouve dans le fichier concepts.txt.

3. Programmes fournis

Nous vous fournisson un module pour charger en mémoire des images au format JPEG : rdjpeg.h/rdjpeg.c et un programme exemple qui charge une image en mémoire et affiche les plans rouge, vert et bleu du premier bloc 8×8 ce cette image : read_image.c. Il se compile avec :
cc rdjpeg.c read_image.c -o read_image

Un executable Linux djpeg est fourni pour le cas où il ne serait pas disponible sur votre système.

4. Implémentation

Le développement suivra les étapes suivantes (environ une par séance).

4.1. Calcul d'un histogramme de couleur

Écrire une procédure pour calculer un histogramme tridimmensionnel dans l'espace RGB sur une image. La procédure doit avoir un nombre de "bins" paramétrable (#define) pour chaque composante (R, G ou B). En pratique, on considèrera un histogramme 4×4×4.

Pour vérification, l'histogramme RGB 4×4×4 de l'image http://mrim.imag.fr/voc10/images/2008_000015.jpg est :

  0.452789  0.045333  0.039810  0.000000
  0.011272  0.014196  0.012398  0.000037
  0.000000  0.000000  0.000031  0.000000
  0.000000  0.000000  0.000000  0.000000

  0.025755  0.004826  0.000917  0.000000
  0.018153  0.118135  0.017584  0.003156
  0.000000  0.001872  0.002043  0.000312
  0.000000  0.000000  0.000000  0.000000

  0.000049  0.000000  0.000000  0.000000
  0.000018  0.007131  0.001205  0.000116
  0.000000  0.005645  0.145388  0.001853
  0.000000  0.000000  0.003835  0.000361

  0.000000  0.000000  0.000000  0.000000
  0.000000  0.000000  0.000000  0.000000
  0.000000  0.000000  0.006612  0.000147
  0.000000  0.000000  0.035187  0.023835
Ce affichage corresponf à l'ordre h[r][g][b]  :
  h[0][0][0]  h[1][0][0]  h[2][0][0]  h[3][0][0]
  h[0][1][0]  h[1][1][0]  h[2][1][0]  h[3][1][0]
  h[0][2][0]  h[1][2][0]  h[2][2][0]  h[3][2][0]
  h[0][3][0]  h[1][3][0]  h[2][3][0]  h[3][3][0]

  h[0][0][1]  h[1][0][1]  h[2][0][1]  h[3][0][1]
  h[0][1][1]  h[1][1][1]  h[2][1][1]  h[3][1][1]
  h[0][2][1]  h[1][2][1]  h[2][2][1]  h[3][2][1]
  h[0][3][1]  h[1][3][1]  h[2][3][1]  h[3][3][1]

  h[0][0][2]  h[1][0][2]  h[2][0][2]  h[3][0][2]
  h[0][1][2]  h[1][1][2]  h[2][1][2]  h[3][1][2]
  h[0][2][2]  h[1][2][2]  h[2][2][2]  h[3][2][2]
  h[0][3][2]  h[1][3][2]  h[2][3][2]  h[3][3][2]

  h[0][0][3]  h[1][0][3]  h[2][0][3]  h[3][0][3]
  h[0][1][3]  h[1][1][3]  h[2][1][3]  h[3][1][3]
  h[0][2][3]  h[1][2][3]  h[2][2][3]  h[3][2][3]
  h[0][3][3]  h[1][3][3]  h[2][3][3]  h[3][3][3]
Le résultat doit ensuite être stocké au format libsvm. C'est un format dans lequel une étiquette et un vecteur sont représenté sur une ligne avec un codage optimisé pour les vecteurs creux. La représentation est de la forme <étiquette> (<numéro de composante>:<valeur de la composante>)*. Seules les composantes non nulles sont mentionnées. La numérotation des composantes commence à 1. On met 0 dans le champ <étiquette>. Pour la même image que ci dessus, ça donne (une seule ligne)  :
0 1:0.452789 2:0.045333 3:0.039810 5:0.011272 6:0.014196 7:0.012398 8:0.000037 11:0.000031 17:0.025755 18:0.004826 19:0.000917 21:0.018153 22:0.118135 23:0.017584 24:0.003156 26:0.001872 27:0.002043 28:0.000312 33:0.000049 37:0.000018 38:0.007131 39:0.001205 40:0.000116 42:0.005645 43:0.145388 44:0.001853 47:0.003835 48:0.000361 59:0.006612 60:0.000147 63:0.035187 64:0.023835
Quand la méthode est validée pour une image, il faut lancer le calcul sur toutes les images des collections d'entraînement et de validation. On produit un fichier par collection avec une ligne de vecteur par ligne dans le fichier urls.txt. La correspondance se fait par l'ordre qui doit être le même. Valider la procédure avec une cinquantaine d'images et ensuite lancer une fois le calcul sur chaque collection complète.

Attention : si vous traitez toutes les images dans un même programme, il faut libérer les tableaux alloués par read_cimage() à chaque lecture en utilisant la fonction free_cimage() sinon la taille de votre programme en mémoire va exploser et il pourrait échouer à cause de cela (actualiser rdjpeg.h/rdjpeg.c si nécessaire).

4.2. Classification à partir d'histogrammes de couleurs avec libsvm

Vous ferez la classification par machines à vecteurs de supports (SVM) avec le logiciel libsvm. Le site contient une documentation et une distribution. Une version compilée pour linux 32-bits est installée dans /u/q/quenotg/local/linux/bin. Vous pouvez aussi installer la distribution sur votre propre machine. Vous utiliserez la version C-SVC avec noyau RBF gaussien.

La mise en œuvre devra suivre les étapes suivantes :

Ceci est à faire pour chacun des 20 concepts cibles. On mettra la méthode au point avec une seul concept (aeroplane) et on l'appliquera aux autres ensuite.

Comme les concepts cibles sont peu fréquents, la meilleure prédiction pour libsvm est de toujours attribuer la classe négative. Pour éviter cela, il faut mettre des poids élevés (19) pour la classe positive (option -w+1 19). Attention: 19 n'est pas forcément la valeur optimale. En théorie, la valeur optimale est le rapport entre le nombre d'échantillons annotés comme négatifs et le nombre d'échantillons annotés comme positifs. En pratique, la valeur optimale peut être différente mais ce rapport est en général une bonne solution. Cette option doit être utilisée seulement dans svm-train.

Une mesure de performance différente doit aussi être choisie (cela sera fait dans la séance suivante), elle prendra en compte un ordre parmi les images retournées. Afin aussi de pouvoir trier les images de validation de la plus probable à la moins probable, il faudra utiliser la version « probabilisée » (option -b 1). Cette option doit être utilisée dans svm-train ET dans svm-predict.

Vous pouvez essayer différentes combinaisons de paramètres pour améliorer la performance de classification. En particulier, il vaut mieux mettre le paramètre gamma avec une valeur de 1 (option -g 1.0).

Séance 2:
programme pour générer un fichier de vecteurs .svm "neutre" :
- urls.txt -> color.svm (train et val)
programme pour générer un fichier de vecteurs .svm avec annotations :
- color.svm, aeroplane.ann (train seulement)
doivent être prêts pour la séance suivante :
- color.svm (train)
- color.svm (val)
- color_aeroplane.svm (train)

Séance 3 :
application de svm-train :
- color_aeroplane.svm -> color_aeroplane.model (train seulement)
application de svm-predict :
- color.svm(val), color_aeroplane.model (train) -> color_aeroplane.out (val)
application aux autres concepts (faire des scripts)

Séance 4 :
programme pour mise au format trec_eval :
- color_aeroplane.out -> color_aeroplane.top
évaluation avec trec_eval :
- aeroplane.rel, color_aeroplane.top -> AP(color_aeroplane) (performance sur color_aeroplane)
évaluation de tous :
- all.rel, color_all.top -> MAP (performance color globale)

Séance 5:
génération des histogrammes de SIFT :
- programme créer le fichier d'échantillons SIFT pour le clustering ;
- lancement du programme de clustering via la commande R ;
- génération des histogrammes de SIFT pour l'apprentissage par svm.

Séance 6 :
apprentissage par svm des concepts (similaire à la séance 4) : commencer par aeroplane.



4.3. Évaluation (MAP et P@N avec trec_eval)

Vous ferez l'évaluation selon la métrique MAP (Mean Average Precision) avec le logiciel trec_eval. L'archive contient une distribution. Une version compilée pour linux 32-bits est installée dans /u/q/quenotg/local/linux/bin. Vous pouvez aussi installer la distribution sur votre propre machine.
Usage: trec_eval [-h] [-q] {-m measure}* trec_rel_file trec_top_file
   -h: Give full help information, including other options
   -q: In addition to summary evaluation, give evaluation for each query
   -m: calculate and print measures indicated by 'measure'
       ('-m all_qrels' prints all qrels measures, '-m official' is default)
trec_eval compare une référence (trec_rel_file, la bonne réponse) et une soumission (trec_top_file, la prédiction du système).

Le programme attend ses entrées sous une format spécifique malheureusement différent du format des annorations pour la référence et différent du format de sortie de libsvm pour les soumissions. La conversion est faite pour les annotations et se trouve dans val/rel avec un fichier par concept plus un fichier pour l'ensemble color_all.rel. Il faudra faire vous-même la conversion pour la prédiction du système.

Le format comprend une ligne par image et par concept (il peut y en avoir un seul). trec_top_file contient une liste ordonnée. L'ordre est défini par un score qui sert à trier les images (du plus grand au plus petit score). Le format de la ligne est :

<concept_id> Q0 <image_id> 0 <score> R
Les champs "Q0", "0" et "R" ne servent à rien. Ils sont là pour des raisons "historiques".

Le score peut être la probabilité produite par svm-predict avec l'option -b.

Pour construire le fichier .top, il est plus pratique d'utiliser le fichier histo.svm dans svm-predict et le fichier list.txt en utilisant la correspondance ligne à ligne.

Ceci est à faire pour chacun des 20 concepts cibles. On mettra la méthode au point avec une seul concept (aeroplane) et on l'appliquera aux autres ensuite. Attention, l'ordre des classes peut changer dans le fichier de sortie de libsvm, il faut vérifier la ligne labels (la première).

4.4. Calcul de SIFTs locaux (programme ou résultats fournis)

Il existe des approches qui permettent d'extraire des carcatéristiques visuelles, basées sur des informations localisées autour d'un point d'une image. Ces approches proposent également des manièeres de déterminer autour de quel point travailler et également à quelle distance autour du point effectuer l'extraction de caractéristique.

L'approche SIFT (pour Scale Invariant Feature Transform) et une approche qui donne de très bons résultats. Il existe quelques programmes qui réalisent cette extraction, comme par exemple colorDescriptor de Koen van de Sande (vous pouvez regarder cela en dehors du TP). Comme ce programme est assez lent (quelques secondes par image...). Nous vous proposons d'utiliser les fichiers générés par ce programme comme source pour votre travail.

Une difficulté de ces approches est qu'il n'y a pas un nombre prédéfini de caractéristiques extraites pour chaque image (à l'inverse de ce qui se passe pour les histogrammes de couleurs vous avez calculés). Il faut alors avoir recours à une étape de clustering (point suivant).

Le programme de Koen van de Sande génère une liste de caractéristiques à 128 dimensions, selon le format suivant :

KOEN1
128
< entier : nb de caractéristiques extraites >
(< ligne de caractéristiques >)*
avec une ligne de caractéristiques telle que :
<CIRCLE <entier> <entier> <réel> <entier> <réel>>;entier (entier)*;
Par exemple :
<CIRCLE 159 154 1.78381 0 0.000296159>; 0 0 31 20 8 0 0 0 0 2 13 13 2 0 10 10 0 4 22 2 0 7 32 4 0 2 18 3 1 4 12 0 7 0 0 6 16 0 0 3 99 7 0 1 2 1 59 140 22 4 0 0 0 100 140 69 0 0 0 0 0 44 140 11 11 4 0 6 16 0 0 0 140 140 19 0 1 0 6 33 56 114 140 15 1 35 75 22 0 8 140 16 1 15 116 9 0 1 0 4 14 0 0 0 10 62 28 1 1 0 0 0 1 39 140 7 0 0 0 0 8 3 140 7 0 0 5 18;

qui veut dire que dans la région de centre (159,154) une caractéristique à été extraite à une échelle 1,78381 avec une orientation de 0 et un degré de visibilité de 0.000296159.

Afin de pouvoir utiliser ces caractéristiques pour le clusterinhg, il faut les mettre en forme, c'est-à-dire éliminer les infomations autres que les caractéristiques elles-mêmes. Pour cela, on élimine les entêtes (les 3 premières lignes d'un fichier généré par colorDescriptor), on enlève aussi la partie "tag" des lignes de caractéristiques, sans oublier d'enlever les ";". TRES IMPORTANT : une ligne ne doit pas se terminer par un espace (le retour charriot doit être juste après la dernière dimension).

Il vous est donc demandé de créer, pour chaque fichier sift de l'ensemble train fourni en /u/m/mulhemp/sift/train/, les fichiers nettoyés en ne gardant qu'une ligne sur 75. Il vous est demandé d'eviter de générer toutes les lignes pour des raisons de place occupée sur disque. De plus, un fichier liste_train_sift donnant toute la liste est dans ce même répertoire /u/m/mulhemp/sift/train/.

Ensuite, vous aller concaténer tous ces fichiers (rappellons que ces fichiers ne contiennent qu'une ligne de caractéristiques toutes les 75 par exemple) afin de créer un fichier

samples.txt
qui n'a pas trop d'échantillons (plus le nombre d'échantillons est grand, plus le clustering va nécessiter de la mémoire et plus le traitement sera long, jusqu'à... plusieurs jours, et des dizaines de GB...).

C'est ce fichier qui va être utiliser pour le clustering.

4.5. Calcul de sacs ds SIFT (clustering et mapping)

Clustering

L'objectif du fichier samples.txt est de générer des regroupements (clusters) on en fixe a priori le nombre. Pour cela, nous allons utiliser le logiciel R

R est un environnement gratuit pour des calculs statistique. Il permet en particulier de réaliser du clustering automatique.

Nous vous fournissons ici les instructions R qui permettent de réaliser un clustering de type Kmoyennes (Kmeans en anglais). Pour cela, vous devez récupérer le code kmeans_clustering.R, et de l'utiliser de la manière suivante :

R --slave --no-save --no-restore --no-environ --args ./samples.txt 256 ./centers256.txt 10 < kmeans_clustering.R

Cet appel indique que l'on utilise R en mode non interactif, avec pour arguments : le fichier samples.txt, le nombre de clusters attendu (ici 256), le fichier qui va stocker les centroïdes des clusters, et enfin le nombre d'itérations pour le clustering (ici 10).

Le résultat de cet appel est donc la génération des centroïdes de chaque cluster. Cette étape est longue et peut prendre 10 minutes pour s'exécuter, évitez donc de le lancer plusieurs fois. Il faut ensuite passer en revue chaque caractéristique visuelle extraite pour lui assigner son cluster, c'est l'objectif de l'étape suivante : le mapping.

NOTE : R n'est pas installé sur toutes les machines de l'ufr. Il est installé sur mandelbrot, vous devez donc vous connecter sur mandelbrot pour l'utiliser..

Mapping

Le mapping consiste à déterminer, pour chaque caractéristique visuelle extraite, le cluster qui lui correspond (dont le centroide est le plus proche.

Pour cela, nous allons encore faire appel à R, avec le code qui vous est fourni en 1nn.R. Le nom 1nn vient du fait que l'on trouve, pour une caractéristique le plus proche voisin, c'est-à-dire le "One Nearest Neighbour" (1nn). Ce code appelle R et lui demande, pour chaque élément d'une liste de caractéristriques, de renvoyer l'identifiant du cluster auquel il est rattaché. NOTE : le numéro de cluster commence à 1.

Un utilisation type de ce code est :

R --slave --no-save --no-restore --no-environ --args centers256.txt 256 all_for_R_demo_30 res1nn.txt < 1nn.R

qui indique que R attend le fichier de centroïdes (généré précédemment), qu'il travaille sur 256 dimensions (clusters), qu'il doit s'appliquer sur un fichier appelé ici all_for_R_demo_30 et mettre de résultat dans le fichier res1nn.txt. Bien entendu, les noms de fichiers sont indicatifs.

Pour un fichier de caractéristiques composé de 3 lignes:

0 0 1 0 0 0 0 0 0 0 0 1 13 1 0 0 1 0 0 0 41 8 3 4 6 8 1 6 74 1 0 0 1 28 29 12 2 3 2 0 8 9 48 34 40 14 4 4 8 2 2 3 172 94 74 19 65 29 4 16 172 109 9 14 0 9 11 8 7 35 31 2 26 11 23 16 11 32 41 49 23 3 2 9 169 64 18 30 38 0 0 1 172 172 132 73 1 14 33 9 2 6 9 2 6 13 49 17 0 5 13 11 9 10 15 13 33 24 7 7 0 0 5 7 27 77 107 20
2 0 0 0 2 0 0 2 14 3 1 0 0 0 3 26 14 17 29 28 10 0 0 4 0 0 3 20 17 0 0 0 57 1 0 4 5 0 0 21 119 18 10 6 20 43 40 119 21 35 119 119 96 65 12 13 0 0 23 83 35 2 0 0 70 21 0 1 14 15 4 3 119 119 106 21 32 30 11 27 16 38 62 43 119 119 39 6 1 0 1 3 8 108 23 1 6 9 1 4 36 23 5 0 14 59 70 2 0 1 1 0 119 119 41 11 6 41 25 12 25 20 17 52 83 119 26 9
9 22 23 10 0 0 0 0 21 23 24 20 2 0 0 0 5 7 12 46 28 1 1 0 0 0 0 2 10 4 9 16 60 61 99 87 27 0 0 10 115 115 115 115 9 0 0 6 58 43 92 115 52 8 29 21 17 9 2 30 45 36 84 83 44 27 14 81 38 1 1 7 85 51 31 81 26 0 0 11 73 115 31 71 17 0 3 8 28 111 25 56 78 25 10 7 2 1 0 6 12 6 115 90 0 3 22 34 20 3 19 71 0 8 43 17 14 1 0 78 0 12 51 32 43 12 3 49 0

Le résultat généré est de la forme :

189
101
146

Qui indique que la première caractéristique est assignée au cluster numéro 189, la seconde au cluster 101 et la troisième au cluster 146.

Vous devez donc, à partir du fichier de caratéristiques de chaque image de train, générer le fichier de mapping et le stocker.

Pour simplifier votre problème, nous vous fournissons le code (shell script) qui permet de génerer les fichiers de mapping en http://mrim.imag.fr/GINF53C4/PROJET/process_1nn_sift.sh. Ce code suppose que :

Vous pouvez changer cela en éditant ce fichier. Vous devez faire la même chose pour les fichiers dans les répertoires val.
ATTENTION : pour les fichiers val il NE FAUT PAS relancer kmeans_clustering.R ; mais vous réutilisez le fichier centers256.txt généré sur le train pour appliquer le 1nn.R sur les fichiers de val.

4.6. Classification à partir de sacs de SIFT et évaluation

A partir des fichiers de mapping, vous allez pouvoir créer un fichier d'histogrammes en comptant combien de fois chaque cluster apparait dans une image et en normalisant ce vecteur (s'il y a 50 SIFTS pour une image et qu'il y a 2 SIFTS dans le cluster 1, alors la valeur de cette dimension dans le vecteur sera égale à 2/50 = 0.04). Cela va ensuite vous permettre de relancer le même genre d'expérimentations qu'à partir du 4.1, mais à base de SIFT. Le mieux est de générer directement les fichiers histogrammes au format libsvm comme pour les histogrammmes de couleurs.

Pour les images de val, il faut également créer les fichiers de mapping et le fichier d'histogrammes. On utilise le même fichier de centroides de clusters que pour l'ensemble train. Les caractéristiques SIFT extraites sont dans le répertoire /u/m/mulhemp/sift/val/ qui contien le fichier liste_val_sift des fichiers sifts pour l'ensemble de validation.

Une fois que les fichiers ".svm" sont créés pour les collections train et val, la même méthode que pour les histogrammmes de couleurs doit etre appliquée pour la production des fichiers ".svm" d'entraînement par concepts, la production de modèles par concepts, la prédiction de scores par concepts et l'évaluation par concepts ou globale.

4.7. Fusion tardive et évaluation

Pour la fusion tardive, il faut construire des scores de classification globaux en faisant la moyenne entre les scores de claffisication obtenus par les histogrammmes de couleur ou par les histogrammes de SIFTs. Le résultat doit être évalué de la même façon. Il faut utiliser les scores de classification transformés en probabilités. Il faut comparer la performance obtenue (MAP) pour chacun des descripteurs pris séparément et celle obtenue par la fusion.

4.8. Classification d'une image quelconque

Vous pouvez faire un programme qui prend en entrée le chemin dans le système de fichier ou l'URL d'une image quelconque et qui fournit les scores de classification (probabilités) pour chacun des 20 concepts. Affichage du plus probable au moins probable avec la probabilité associée.

Ce programme peut tourner sur le serveur web goedel ou en ligne de commande. Il peut prendre en paramètre des otpions spécifiant une recherche seulement par la couleur, seulement par la texture ou une combinaison des deux. Si vous voulez faire une interface web, vous pouvez partir de l'exemple form_post.html appelant le code post.c avec le module cgiu.h / cgiu.c

Pour le calcul des descripteurs SIFT, vous pouvez utiliser le programme colorDescriptor en appel system(). colorDescriptor n'a pas d'option pour prendre l'image sur l'entrée standard et envoyer le résultat sur la sortie standard. Il faut obligatoirement passer par le système de fichiers. La commande est :

colorDescriptor --descriptor sift <image>.jpg --output <image>.sift

4.9. Optionnel (non noté) : test de descripteurs "deep learning"

Les fichiers train/deep294.svm et train/val294.svm contiennent des descripteurs à 294 dimensions obtenus par apprentissage profond (deep learning). Ils sont proches de l'état de l'art actul en matière de descripteurs visuels. Ils donnent normalement des résultats encore meilleurs que les histogrammes de couleur et de SIFT (mais ils sont beaucoup plus complexes à calculer). Vous pouvez aussi les tester.

5. Évaluation du projet

Vous devrez faire un compte rendu et nous fournir les codes sources de vos programmes et les différents scripts que vous utilisez pour les lancer. Vous nous enverrez le compte-rendu et les sources (pas les fichiers de données) dans une archive zip ou tgz pour le 10 avril au plus tard (Georges.Quenot@imag.fr, Philippe.Mulhem@imag.fr). Vous nous montrerez ce qui tourne lors de la séance du 29 mars.