Chapitre 3 TP Python I : Transformations géométriques sur des images
Le but de ce TP est d’appliquer les transformations géométriques que nous avons vues en cours sur des images.
Le principe de rendu sera le même que précédemment : Un fichier Jupyter rendu au plus tard une semaine après la dernière séance sur Moodle.
Pour rappel, pour lancer Jupyter Lab, vous avez les options suivantes :
- Utiliser la commande jupyter lab (ou jupyter notebook) dans un terminal sous linux.
- Sous Windows, on suit le chemin
C:\winpython\jupyterlab
- Utiliser un IDE qui supporte les notebooks Jupyter comme PyCharm ou Visual Code Studio
- Utiliser la version en ligne : https://jupyter.org/try-jupyter/lab/?path=notebooks
Dans tous les cas, on demandera bien un noyau Python et on utlisera la syntaxe Markdown pour mettre en forme le document.
Chaque fonction créée sera illustrée avec un ou plusieurs exemples. Il sera indispensable d’avoir un stylo et une feuille de brouillon pour réfléchir et trouver les formules avant d’écrire du code.
3.1 Importation et affichage des images
Une image de taille \(n\times m\) pixels peut-être vue comme une matrice de taille \(n\times m\) où chaque coefficient est un triplet \((r,v,b)\) où \(r\), \(v\), \(b\) correspondent aux niveaux de rouge, vert et bleu. Souvent la profondeur de couleur est 8 bits, ce qui fait que chaque niveau de couleurs est un nombre entier entre 0 et 255. Cela fait \(256=2^8\) possibilités et en binaire, c’est représenté par un nombre à 8 chiffres.
Ainsi, nous manipulerons les images comme des objets de type ndarray de la bibliothèque Numpy. Ce sont des objets comme ceux de type array sauf que les coefficients pourront avoir une dimension plus grande (3 pour nous).
On commencera par importer numpy (avec éventuellement un raccourci).
Pour importer les images, on utilisera matplotlib (il y a bien d’autres bibliothèques qui permettent de manipuler des images mais c’est le choix que l’on fait pour ce TP).
On peut alors importer des images dans Python.
Pour afficher l’image, on utilise la commande suivante.
Il y a des nombreuses options (pour les axes, titre, échelles,…) mais nous n’utiliserons que la fonction imshow() qui affiche une image. On obtient une image comme celle-ci :
On fera très attention aux axes ! La première coordonnée correspond à l’axe vertical dirigé vers le bas et la seconde à l’axe horizontal vers la gauche. C’est-à-dire que les coordonnées se comportent comme les indices d’une matrice (et commencent à 0).
Si on veut connaître les niveaux pour un pixel particulier, on écrit la même commande que pour obtenir le coefficient d’une matrice
Le niveau de rouge du pixel de coordonnées (50,50) est ainsi 159. Le niveau 0 correspond à l’absence de la couleur et 255 au niveau maximal de cette couleur. Par exemple [0 0 0] correspond à un noir pur et [255 255 255] à un blanc pur.
Exercice 3.1 Deux images sont fournies sur Moodle dans la partie concernant ce TP.
- Choisir une image de taille environ 1280 pixels de large (format HD),
- L’importer,
- L’afficher et récupérer les niveaux des quatres coins (on indiquera à quels coins cela correspond).
Exercice 3.2 (centre) La méthode .shape() retourne les dimensions d’un ndarray. Pour récupérer les dimensions d’une image appelée image, on pourra utiliser la commande image.shape([0:2]). La fonction int() retourne la partie entière d’un nombre à virgule flottante (ce qui sera utile pour bien avoir des coordonnées entières).
- Créer une fonction qui prend en entrée une image et retourne les coordonnées du centre de cette image.
- Retourner les niveaux de couleurs au centre de l’image précédemment choisie.
3.2 Création des matrices et transformations mathématiques
Pour les différents exercices, lorsqu’une matrice est demandée elle sera de la classe de numpy (dans le but de pouvoir utiliser le produit matriciel de numpy). Tous les points et les vecteurs seront simplement représentés par un couple de coordonnées.
Exercice 3.3 (Translation) Créer une fonction translation(vecteur, point) qui prend en entrée un vecteur et un point et retourne l’image du point par la translation de vecteur donné. Les vecteur et point seront des couples de coordonnées. Vous pouvez les transformer en classe array de numpy, utiliser l’addition de numpy et retourner un couple avec la fonction tuple() qui transforme un objet de type array et le transforme en un uplet (tuple en anglais).
Exercice 3.4 (Homothétie) Créer une fonction homothetie(centre, rapport, point) qui prend en entrée le centre, le rapport d’une homothétie, un point et retourne l’image du point par l’homothétie de centre et de rapport donnés.
Exercice 3.5 (Matrice de rotation) Créer une fonction matrice_rotation(angle) qui prend en entrée un angle en degrés et retourne la matrice de la rotation pour cet angle. On utilisera la bibliothèque math avec les fonctions cos, sin et radians. Les fonctions sinus et cosinus prennent en argument des radians. Il faudra donc convertir les degrés en radians.
Exercice 3.6 (Rotation) Créer une fonction rotation(centre, angle, point) qui prend en entrée le centre de la rotation, son angle, un point et retourne l’image du point par la rotation de centre et d’angle donnés. On pourra faire appel à la fonction créée à l’Exercice 3.5.
Exercice 3.7 (Symétrie (facultatif)) Créer une fonction symetrie(point,coefficients_droite) qui prend en entrées un point et les coefficients \((a,b,c)\) de la droite \(D\) d’équation \(ax+by+c=0\) et retourne l’image du point par la symétrie d’axe \(D\).
3.3 Transformations géométriques sur les images
3.3.1 Principe
Le but est maintenant d’appliquer ces transformations géométriques à des images. Le principe sera le même pour toutes les transformations. On aura deux images ancienne_image et nouvelle_image. L’ancienne image est donnée et on veut construire la nouvelle image. On partira ici du principe que la nouvelle image a exactement les mêmes dimensions que l’image initiale.
Les étapes seront alors les suivantes :
- Récupérer la taille de l’image initiale avec une commande du type (et comprendre l’argument !)
- Créer une image noire avec la bonne taille (np.uint8 indice que le niveau de chaque niveau sera donné avec un nombre de 8 bits) :
- Remplir les niveaux de couleurs pour la nouvelle image. Pour chaque pixel de coordonnées \((x,y)\) on va mettre les niveaux de couleurs du pixel de coordonnées \((x',y')\) de ancienne_image. La relation entre les coordonnées est
\[(x,y)=T(x',y')\] où \(T\) est la transformation (rotation, homothétie, translation,…) que l’on souhaite appliquer. On a donc besoin des coordonnées \((x',y')\) que l’on obtient avec la relation inverse :
\[(x',y')=T^{-1}(x,y).\]
On va donc faire une boucle sur tous les couples \((x,y)\) de coordonnées, on vérifie qu’en appliquant \(T^{-1}\) on tombe bien dans les dimensions de l’ancienne image et on récupère les valeurs de niveaux de couleurs.
Les pixels dont l’image par \(T^{-1}\) tombent en dehors de l’image initiale restent noirs. Par exemple, pour une rotation du Louvre, on obtient une image du type suivant.
Donnons un exemple si \(T\) est l’homothétie de rapport 2 centrée en \((0,0)\), c’est-à-dire \(T(x,y)=(2x,2y)\). On aura alors une formule du type
L’apparition de la fonction int() vient du fait que l’on veut toujours des coordonnées entières. En général, la formule fera appel aux fonctions que vous aves créées précédemment.
3.3.2 Exercices
Exercice 3.8 (Homothétie) Écrire une fonction homothetie_image(image,rapport,centre) qui prend en entrées une image, un rapport et un centre et retourne l’image transformée par l’homothétie de centre et de rapport donnés en arguments.
Exercice 3.9 (Rotation) Écrire une fonction rotation_image(image,angle,centre) qui prend en entrées une image, un angle et un centre et retourne l’image transformée par la rotation de centre et d’angle donnés en arguments.
Exercice 3.10 (Symétrie (facultatif)) Écrire une fonction symetrie_image(image,coefficients_droite) qui prend en entrées une image et le triplet \((a,b,c)\) des coefficients de la droite d’équation \(ax+by+c=0\) et retourne la transformation de l’image par la symétrie d’axe la droite donnée.