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.