Jouer avec les Images

Transcription

Jouer avec les Images
IUT d'Orsay, Départementt Informatique
Algorithmique et C++, S1, 2011-2012
Mini-Projet n°2 : Jouer avec les images.
A rendre pour le lundi 14 novembre 2011 avant midi.
Introduction :
Une image est un ensemble de pixels dont chacun est défini par trois valeurs, que l'on note R, G, et
B. Ces valeurs représentent l'intensité des couleurs rouge, vert et bleu (R pour red, G pour green, et B
pour blue) et déterminent la couleur du pixel. Elles sont comprises entre 0 et 255. Chaque couleur est
associée à un unique triplet (R, G, B). On appelle composante rouge d'un pixel (respectivement verte,
bleue), la valeur R (respectivement G, B) dans le triplet (R, G, B).
Dans ce projet, une image sera représentée par trois tableaux 2D, notés R, G, B, tous de même taille
la taille de l'image, et représentant chacun respectivement la composante couleur rouge, verte, ou bleue
de l'image. Ainsi, chaque case d'un de ces tableaux contiendra la valeur R du pixel si l'on considère le
tableau R, la valeur G si l'on considère le tableau G, ou bien la valeur B si l'on considère le tableau B.
Les données sont stockées de haut en bas et de gauche à droite. Par exemple, l'image suivante est
représentée par les trois tableaux suivants, tous des tableaux de taille 3x2 :
Tableau R :
255
0
0
255 255
0
Tableau G :
0 255 255
255 255
0
Chaque carré représente un pixel
Tableau B :
0
0 255
0 255
0
But du projet :
Le but de ce mini-projet est de vous fournir une introduction et une initiation au traitement de
l'image. L'objectif est de pouvoir manipuler des images, afin d'avoir une idée de comment est codée une
image et de ce qu'il est possible de faire avec.
Vous apprendrez entre autres à travailler les couleurs (contraste, inversion de couleurs), à travailler
sur la géométrie de l'image (symétrie, rotation) et à appliquer certains filtres pour par exemple
détecter les contours des objets de l'image ou encore flouter l'image.
Vous devrez écrire un programme qui manipule les images et réalise toutes ces opérations. Pour cela,
vous compléterez au fur et à mesure le programme images.cpp qui se trouve sur Dokéos.
Ce programme manipule des tableaux de capacité maximale MAX et de taille effective taille. Par
défaut, MAX vaut 150. Ainsi, toutes les images manipulées seront des images carrées de côté 150
pixels, et dans la plupart des cas, taille aura la même valeur que MAX, c'est à dire que les tableaux
seront pleins.
Ce programme contient deux fonctions, LoadPicture, qui prend en argument le nom de l'image à
charger ainsi que 3 tableaux R, G et B, et qui remplit les tableaux avec les données de l'image
correspondante, et WritePicture, qui prend en argument trois tableaux R, G et B, ainsi que le nom de
l'image à créer, et qui crée cette image à partir des données disponibles dans le tableau.
Vous devrez appeler ces fonctions de la manière suivante :
LoadPicture (string nom_de_l'image, int tab_R, int tab_G, int tab_B)
et WritePicture (int tab_R, int tab_G, int tab_B, string nom_de_l'image).
IUT d'Orsay, Départementt Informatique
Algorithmique et C++, S1, 2011-2012
Toutes les images que nous utiliserons dans ce projet seront carrées de taille 150 pixels par 150
pixels, et seront au format ppm (portable pixmap). Pour plus de détails sur ce format, on pourra lire
l'annexe (facultatif).
Vous pourrez visualiser vos images avec Gimp (par exemple), qui est un visionneur d'images gratuit et
disponible sur internet. Nous mettons à votre disposition plusieurs images, cependant, vous pouvez
utiliser vos propres images. Pour cela, convertissez les au format .ppm avec Gimp et redimensionnez
les : elles doivent être de largeur 150 pixels et de hauteur 150 pixels.
Pour toutes questions relatives au sujet, vous devez utiliser le forum accessible via Dokéos dans la
rubrique: IUT-ORSAY-DUT-INFORMATIQUE-S1 > Forums > Algo Langage > Mini Projet 1.
0. Recopiez depuis Dokéos le programme image.cpp (qui se trouve dans le répertoire miniprojets) sur votre répertoire de travail. Complétez ensuite le programme pour
récupérer les données de l'image de votre choix (à l'aide de la fonction LoadPicture) et
créez une image « cp.ppm » à partir des tableaux ainsi initialisés (à l'aide de la fonction
WritePicture).
Partie 1 : Opérations sur les couleurs :
1.
Complétez votre programme afin de créer une image « r.ppm » contenant uniquement la
composante de couleur rouge de l'image. On créera de même les images « g.ppm » et
« b.ppm » contenant uniquement les composantes vertes et bleues respectivement.
2. Complétez votre programme afin de créer une image « rg.ppm » qui contiendra les
composantes rouge et verte de l'image. Faire de même avec le rouge et le bleu dans une
image « rb.ppm » et avec le vert et le bleu dans une image « gb.ppm ».
3. Complétez votre programme afin qu'il recherche la présence d'une couleur donnée dans
une image. En fin de traitement, votre programme devra dire si la couleur recherchée
(rouge, verte, ou bleue) est présente ou non dans l'image, et si oui, afficher à l'écran les
coordonnées du premier pixel contenant du rouge. Attention, on lit les images de haut en
bas et de gauche à droite. Le pixel situé sur la première ligne dernière colonne est avant
celui situé sur la deuxième ligne première colonne.
On constituera un jeu d'essais pour cette question.
4. Pour avoir le négatif d'une photographie, il suffit d'appliquer à chaque pixel la
transformation suivante : si x est la valeur d'une composante d'un pixel donné, alors
255 – x est la valeur de la composante du pixel du négatif. Complétez votre programme
afin de créer le négatif « neg.ppm » de votre image.
5. Pour transformer une image couleur en noir et blanc (cette opération s'appelle la
binarisation), il suffit de se donner un entier SEUIL et si la composante rouge du pixel
est supérieure au seuil, alors on met les trois composantes RGB du pixel à 255, sinon on
les met toutes à 0.
Complétez le programme précédent et créez une image « bin.ppm » qui est la
binarisation de l'image chargée avec la valeur SEUIL = 100.
6. Pour modifier le contraste d'une image, il suffit de multiplier chaque composante de
chaque pixel par un même réel strictement positif a. Si après cette opération, il y a un
pixel dont une composante est supérieure à 255 (valeur maximale pour R, G ou B), alors
on mettra cette composante à 255.
Complétez le programme précédent et créer une image « ctr.ppm » qui est le résultat de
l'opération précédente : on multipliera toutes les valeurs par un réel a = 2 en faisant
IUT d'Orsay, Départementt Informatique
Algorithmique et C++, S1, 2011-2012
attention à mettre les valeurs à 255 en cas de dépassement de la valeur maximale.
Modifier votre programme avec a = 0,8. Que constatez-vous ? Expliquez en quelques
mots comment éclaircir ou foncer une image ?
Partie 2 : Transformations géométriques :
7. Le symétrique d'une image par rapport à un axe vertical est l'image obtenue à partir de
cette image en mettant un miroir le long d'un côté (gauche ou droit) de l'image. Ainsi, la
droite se retrouve à gauche et la gauche se retrouve à droite.
L'exemple ci-dessous illustre cette notion :
Image originale
Image après modification
On définit de même le symétrique par rapport à un axe horizontal comme étant l'image
obtenue en mettant un miroir en haut ou en bas de l'image. Ainsi, le haut se retrouve en
bas et le bas se retrouve en haut. L'exemple ci dessous illustre cette notion :
Complétez votre programme afin d'obtenir les images « sv.ppm » qui est le symétrique
par rapport à un axe vertical de votre image d'origine et « sh.ppm » qui en est le
symétrique horizontal.
8. La rotation à 90° d'une image est l'image obtenue en tournant d'un quart de tour par
rapport au sens inverse des aiguilles d'une montre l'image d'origine.
L'exemple ci-dessous illustre cette notion :
Rotation à 90°
Symétrie d'axe horizontal
IUT d'Orsay, Départementt Informatique
Algorithmique et C++, S1, 2011-2012
Complétez votre programme afin d'obtenir l'image « rot.ppm » qui est la rotation à 90°
de votre image d'origine.
Partie 3 : Application de filtres :
L'application d'un filtre à une image permet de modéliser bon nombre de phénomènes comme la
détection de contours ou encore le floutage.
Appliquer un filtre à une image consiste à modifier les pixels de la manière suivante : chaque pixel
est modifié en fonction de ses voisins.
Un filtre est représenté sous la forme d'un tableau de taille nxn, où n est un entier positif, le plus
souvent égal à 3. Il est en lien direct avec le nombre de voisins qui interviennent dans la modification de
l'image.
A titre d'exemple, le filtre suivant modifie un pixel de la manière suivante : Si entrée(i, j) désigne le
pixel en position (i, j) sur l'image de départ et sortie(i, j) le pixel en position (i, j) en sortie, alors :
sortie(i, j) = 1/4(entrée (i-1, j-1) + entrée(i, j) + entrée(i+1, j) + entrée(i, j+1)).
Autrement dit, le pixel en sortie est la moyenne des valeurs de lui même, de son voisin en haut à
gauche, de celui directement à droite et de son voisin du dessous.
¼
0
0
0
¼
¼
0
¼
0
Par exemple, si la tableau suivant désigne la composante rouge d'une image,
0
0
255 198
2
3
178 96
128 127 6
7
132 12
49
0
0
0
0
0
0
77
70
0
0
36
16
0
0
0
0
0
Alors :
Représente la composante rouge de l'image après application du filtre. Les bords n'étant pas traités
par un tel filtre, les pixels sont mis à 0 sur chaque composante.
9. Complétez votre programme et créez une image « f1.ppm » qui correspond à l'image que
vous obtenez en appliquant le filtre suivant à votre image :
1/ 9
1/ 9
1/ 9
1/ 9
1/ 9
1/ 9
1/ 9
1/ 9
1/ 9
Filtre 1
On fera attention à ne pas appliquer ce filtre aux bords de l'image, et donc, à initialiser
toutes les valeurs aux bords à 0.
Ce filtre fait la moyenne de tous les pixels voisins.
Selon vous, à quoi correspond ce filtre ? Que fait-il à l'image ? Répondez en quelques
phrases.
IUT d'Orsay, Départementt Informatique
Algorithmique et C++, S1, 2011-2012
10. Complétez votre programme et créez une image « f2.ppm ». Cette image sera le résultat
de votre image d'origine après application du filtre suivant :
1
0
-1
1
0
-1
1
0
-1
Filtre 3
On fera bien attention à ce que les valeurs des composantes des pixels restent entre 0
et 255 : si la valeur obtenue est négative, on la mettra à 0 et si elle est supérieure à
255, on la mettra à 255.
Selon vous, à quoi correspond ce filtre ? Que fait-il sur l'image ?
11. Complétez votre programme et créez une image « f3.ppm ». Cette image sera le résultat
de votre image d'origine après application du filtre suivant :
1
1
1
0
0
0
-1
-1
-1
Filtre 4
On fera bien attention à ce que les valeurs des composantes des pixels restent entre 0
et 255 : si la valeur obtenue est négative, on la mettra à 0 et si elle est supérieure à
255, on la mettra à 255.
Selon vous, à quoi correspond ce filtre ? Que fait-il sur l'image ?
12. Question facultative :
Ces deux filtres précédents ont en fait des effets complémentaires sur une image. L'un
est dit directionnel en x (selon l'axe des abscisses) et l'autre directionnel en y (selon
l'axe des ordonnées).
Il est possible de combiner les effets de ces deux filtres de la manière suivante :
On rappelle qu'en C++, il faut rajouter en en-tête #include <cmath> pour pouvoir utiliser
des racines carrées. La commande pour la racine carrée est sqrt(double nombre). Par
exemple, pour calculer la racine carrée de 2, on utilisera sqrt(2);
Complétez votre programme et créez une image « f4.ppm » qui combine l'effet de ces
deux filtres.
ATTENTION :
• On n'oubliera pas de commenter son programme : chaque boucle, chaque conditionnelle, chaque
groupe d'instructions doit être précédée d'un commentaire décrivant le traitement.
• Afin de coder proprement et efficacement, veillez a toujours nommer correctement vos
variables et vos constantes, ainsi qu'a bien indenter votre code. N’oubliez pas que ces noms
doivent être intelligibles afin de rendre votre programme lisible
• On n'oubliera pas qu'un tableau a une capacité maximale et une taille effective!
On utilisera la variable taille pour traiter les tableaux.
IUT d'Orsay, Départementt Informatique
Algorithmique et C++, S1, 2011-2012
Travail à rendre par binôme avant le lundi 14 novembre 12h00
Dans le cadre de ce mini-projet, vous devez écrire un seul programme et constituer un dossier qui
contiendra :
1. Une page de titre (nom des binômes, numéro de TP, sujet du projet, date).
2. Une page contenant la table des matières.
3. Une introduction rappelant brièvement le sujet.
4. Les hypothèses de travail et les choix de programmation que vous avez du faire.
5. Les réponses aux questions posées dans le sujet.
6. En cas de difficultés rencontrées ou de problème(s) non résolu(s), fournir les messages
d'erreurs produits et expliquez les tests effectués pour essayer de trouver votre erreur.
Signalez toute partie de votre programme que vous jugez défectueuse.
7. Un jeu d'essais, complet et commenté, respectant les principes décrits dans l’annexe du
TD2, et permettant de convaincre votre lecteur que votre programmation est correcte.
Ce jeu d’essais DEVRA être présente sous forme d’un tableau, comportant les 3 colonnes
suivantes :
● but de l’essai (pourquoi tester ce cas ?)
● la ou les valeurs choisies en entrée (avec quelles valeurs est fait ce test ?)
● les résultats prévus (que devrait-il se passer ? quels résultats devrait-on obtenir ?)
Attention : il n’est pas nécessaire de fournir tous les affichages du programme, il ne s’agit
pas ici de donner une trace d'exécution.
8. Une conclusion, indiquant l'avancement plus ou moins complet du travail, les difficultés
rencontrées, les améliorations et les extensions éventuelles apportées, ainsi que ce que le
projet vous a apporté. On pourra inclure des images au rapport.
9. Annexe 1 : le code source du programme dûment et pertinemment commenté.
10. Annexe 2 : une trace d'exécution du programme portant sur toutes les valeurs du jeu
d'essais, éventuellement commentée à la main, et montrant que le programme s’est comporté
de façon attendue, c'est-à-dire que les résultats obtenus sont identiques aux résultats
attendus décrits dans le jeu d’essai.
Ce dossier devra être remis à votre chargé de TP, soit sous forme électronique à l'adresse email de
votre chargé de TP, soit sous forme papier dans son casier, selon les instructions qu'il vous aura donné.
Vous enverrez par mail à votre chargé de TP le programme que vous aurez fait. Attention, ce
programme doit porter le nom suivant :
Nom1_Nom2_TP(groupe_de_TP)_images.cpp
où Nom1 et Nom2 sont le nom des personnes composant le binôme.
Par exemple : Dupont_Durand_TP152_images.cpp.
Vous remettrez le tout au plus tard le lundi 14 novembre à midi (aucun délai supplémentaire ne sera
accordé, une pénalité de 1 point sera appliquée par jour de retard).
IUT d'Orsay, Départementt Informatique
Algorithmique et C++, S1, 2011-2012
Mini-Projet n°2 : Annexe (facultatif)
Pour aller plus loin et comprendre les fonctions LoadPicture et WritePicture
Introduction :
Une image au format ppm (portable pixmap) est un fichier codé en ASCII, organisé de la manière
suivante :
•
•
•
•
•
•
•
•
•
•
•
Un nombre magique (ici P3) : ce nombre définit le format de l'image
Un caractère d'espacement (espace, tabulation, nouvelle ligne)
Parfois, un commentaire qui commence par #
Un caractère d'espacement (espace, tabulation, nouvelle ligne)
Hauteur de l'image
Un caractère d'espacement (espace, tabulation, nouvelle ligne)
Longueur de l'image
Un caractère d'espacement (espace, tabulation, nouvelle ligne)
La valeur maximale utilisée pour coder les couleurs (en général 255)
Un caractère d'espacement (espace, tabulation, nouvelle ligne)
Données de l'image :
◦ L'image est codée ligne par ligne en partant du haut
◦ Chaque ligne est codée de gauche à droite
◦ Chaque pixel est codé par trois valeurs (rouge vert bleu) en caractères ASCII : la valeur
rouge correspond à l'intensité de rouge, la valeur vert, à l'intensité du vert et la valeur
bleue, à l'intensité du bleu. Par exemple, un pixel de couleur rouge sera codé de la manière
suivante : 255 0 0 ; le 255 correspond à l'intensité du rouge, le 0, à celle du vert et le
zéro de droite, à celle du bleu.
Ces valeurs sont précédée et suivie par un caractère d'espacement.
◦ Aucune ligne ne doit dépasser 70 caractères.
Toutes les lignes commençant par # sont des commentaires qui sont ignorés.
Par exemple, l'image ci dessous est codée de la manière suivante :
P3
#P3 désigne ici le format .ppm
3 2
255
255
0
0
0 255
0
0
255 255
0
255 255 255
0
0 255
0
0
Chaque carré représente un pixel
La fonction LoadPicture a le prototype suivant :
void LoadPicture (string image, int red[MAX][MAX], int green[MAX][MAX], int blue[MAX][MAX]).
Elle lit simplement les données du fichier portant le nom image, qui est de la forme « nom.ppm ».
Les commandes :
permettent de rediriger les entrées standard (qui sont normalement sur la console) dans le fichier
IUT d'Orsay, Départementt Informatique
Algorithmique et C++, S1, 2011-2012
image.
La commande :
permet de remettre l'entrée standard sur la console.
Ainsi, cette fonction lit simplement les données qui sont dans le fichier image et remplit les tableaux
red, green, blue avec les données de l'image.
La fonction WritePicture a le prototype suivant :
void WritePicture (int red[MAX][MAX], int green[MAX][MAX], int blue[MAX][MAX], string name).
Elle écrit simplement les données qui sont dans les tableaux dans le fichier portant le nom name, qui est
de la forme « nom.ppm ».
Les commandes :
permettent de rediriger les sorties standard (qui sont normalement sur la console) dans le fichier name.
La commande :
permet de remettre la sortie standard sur la console.
Ainsi, cette fonction lit simplement les données qui sont dans les tableaux et les réécrit (en tenant
compte du format .ppm) sur le fichier portant le nom name.