Traitement d`images

publicité
TD – Informatique – PSI
Traitement d’images
1. Notions de bases & vocabulaire
Une image numérique est une fonction à support discret et borné, et à valeurs discrètes. Le support est multidimensionnel, en
général 2d ou 3d. Les valeurs peuvent être scalaires (images en niveaux de gris), ou bien vectorielles (imagerie multi-composante,
imagerie couleur).
La gamme de valeurs possibles varie en fonction du type d'images considéré ; le Tableau 1 présente les types les plus courants, les
grandeurs physiques associées et les capteurs utilisés.
Phénomène physique
Lumière visible
Rayonnement Infra-rouge
Echo ultrasonore
Résonance magnétique
Echo électromagnétique
Absorption des rayons X
Grandeur mesurée
Flux photonique émis ou réfléchi
Luminance Infra-rouge (Chaleur)
Distance, densité de tissus,. . .
Présence d'un corps chimique….
Distance, spécularité de surfaces…
Densité de tissus….
Capteur
CCD, CMOS, Scanner
Bolomètres
Echographie, sonar
IRM, RMN,…
Radar, SAR…
Radiographie…
Tableau 1 : Quelques types d’images
Une image numérique est associée à un
pavage de l'espace, en général rectangulaire.
Chaque élément du pavage, appelé pixel, est
désigné par ses coordonnées entières : Figure
1 – gauche. L'échantillonnage est le procédé
de discrétisation spatiale d'une image
consistant à associer à chaque pixel une
unique valeur : Figure 1 – droite. On parle de
sous-échantillonnage lorsque l'image est déjà
discrétisée et que l’on diminue le nombre de
pixels.
Figure 1 Pavage (à gauche) ; Echantillonnage (à droite)
La quantification désigne la discrétisation tonale correspondant
à la limitation du nombre de valeurs différentes que peut
prendre chaque pixel. Une image numérique est donc une image
échantillonnée et quantifiée.
Une image numérique 2D est représentée par un tableau T de h
lignes et w colonnes. Le pixel est désigné par un couple (i,j) où j
est l'indice de colonne j ∈ {0,w−1}, et i l'indice de ligne i ∈
{0,h−1}. w est la largeur, h la hauteur de l'image T. Par
convention, le pixel origine (0,0) est en général en haut à gauche
(voir Figure 2). Le nombre T(i,j) est la valeur (ou le niveau de gris)
du pixel (i,j), T(i,j) ∈ {0,Nmax−1}. Nmax est le nombre de niveaux
de gris. On appelle dynamique de l'image le logarithme en base
2 de Nmax, soit encore le nombre de bits utilisés pour coder
l'ensemble des valeurs possibles.
Figure 2 Conventions de notations
TD – Informatique – PSI
Une image numérique
ne
constitue
donc
qu'une
version
approchée de « l’image
réelle » formée par
l'image
(au
sens
mathématique) de la
projection de la scène 3D
sur la portion de plan
correspondant
à
la
surface photosensible du
capteur. La qualité de
l'approximation dépend
de
la
quantité
d'information portée par
l'image numérique, en
particulier du nombre de
pixels utilisés : la
résolution spatiale. La
Figure 3 montre un
exemple d'une même
image acquise à des
Figure 3 Résolution spatiale : échantillonnage
résolutions différentes.
Le changement de résolution tonale, correspondant à la quantification, fait aussi apparaître une perte d'information dans les
images : voir Figure
4,
pour
la
représentation
d'une même image
avec
différentes
dynamiques.
La
bonne dynamique
dépend
de
la
qualité
des
éléments
photosensibles du
capteur, mais aussi
de la richesse du
contenu
informationnel de
l'image, qui est lié
à la distribution
statistique de ses
valeurs.
Figure 4 Résolution tonale : Quantification
TD – Informatique – PSI
2. Manipulations de base sur les images en noir et blanc
Nous allons nous intéresser dans cette première partie à l’image « damier-ng.jpeg » qui se trouve dans un dossier nommé « Image
» disponible sur le réseau. Copier-coller ce dossier dans le répertoire de travail courant libre d’accès en écriture.
Nous aurons besoin dans toute la suite des bibliothèques « numpy », « image » et « pyplot » de matplotlib. Les importer de la
façon suivante :
import matplotlib.image as im
import matplotlib.pyplot as plt
import numpy as np
Commencer par ouvrir l’image en lecture :
pixels = im.imread("damier-ng.jpeg")
Pour afficher l’image:
plt.figure(1)
plt.imshow(pixels, cmap='gray')
plt.show()
L'image du fichier « damier-ng.jpeg » étant en noir et blanc l'argument cmap='gray' est nécessaire pour afficher l'image
correctement.
Q1. Que représente pixels ? Que représente uint8 ? Que se passe-t-il si on calcule la somme suivante :
np.uint8(10)+np.uint8(250)
Toutes les valeurs du tableau n'ont pas été affichées ci-dessus (elles ne sont pas toutes à 255). La taille du tableau est accessible
par pixels.shape qui fournit dans l'ordre le nombre de lignes et le nombre de colonnes, ou par np.size(pixels,0) et
np.size(pixels,1).
Comme on travaille dans « numpy », il est facile grâce au concept de diffusion que propose cette librairie de modifier un ensemble
de valeurs d’un tableau par une valeur unique. Par exemple : l’instruction pixels[50:250, 150:250] = 128 est
équivalente à pixels[50:250, 150:250] = table où table est un tableau de la même taille que pixels[50:250,
150:250] avec toutes ses valeurs à 128.
Q2. Mettre en œuvre une méthode permettant de créer une nouvelle image avec un bord gris de 50 pixels d’épaisseur tout
autour de l’image de départ.
Nous allons maintenant modifier l'image en inversant l'intensité des pixels. En particulier le blanc et le noir seront inversés.
Q3. Réaliser cette modification et afficher l’image obtenue. Proposer une méthode alternative permettant cette inversion
d’intensité de pixels, sans utiliser de boucles.
3. Matrices de convolutions
Une opération courante en traitement d’image consiste à recalculer la valeur d’un pixel en fonction des valeurs des pixels
environnants. Ceci s'applique par exemple lorsque l'on souhaite flouter une image. Souvent, la nouvelle valeur du pixel est une
combinaison linéaire des valeurs environnantes. On peut alors décrire la transformation par une matrice.
Considérons la matrice suivante :
1 2 4 2 1
1 2 4 8 4 2
𝐾=
4 8 16 8 4
100 2 4 8 4 2
(1 2 4 2 1)
Hormis les pixels trop près du bord de l'image, chaque pixel est le centre d'un carré de 5 pixels de côté. Nous nous proposons de
remplacer la valeur de ce pixel par la moyenne des 25 pixels du carré, en utilisant la matrice K comme coefficients de pondération.
Les pixels proches du centre auront ainsi plus d'influence sur la valeur moyenne.
TD – Informatique – PSI
Pour cela nous allons écrire la fonction filtrer(T,K) qui prend en entrée deux tableaux Numpy : T : l’image à modifier et K :
une matrice carrée de taille impaire 2n+1.
La fonction filtrer(pixels,K) effectue un moyennage des pixels de pixels avec les coefficients de la matrice K. Les pixels
situés sur le cadre extérieur de largeur n ne sont pas modifiés.
Q4. Programmer une cette fonction filtrer(T,K) qui retournera une matrice filtrée de même taille que T, en n’oubliant
pas que T est un objet mutable (il faudra donc faire une copie superficielle de la matrice T dans cette fonction…).
Q5. L'image « ecole-ng.jpeg » contient une scène où apparaît le visage d'une petite fille. Modifier cette image de manière à
flouter le visage. On pourra appliquer plusieurs fois de suite la fonction filtrer afin d'obtenir un flou suffisant pour
masquer l'identité de la fillette.
Les matrices de convolution peuvent aussi être utilisées pour repérer des contours d'objets dans une image. On peut par exemple
utiliser l'une des deux matrices.
−1 0 1
−1 2 −1
𝐺𝑥 = (−2 0 2) OU 𝐺𝑦 = ( 0 0 0 )
−1 0 1
1 2 1
On remarquera que les sommes des coefficients de ces matrices sont nulles. Si on applique la fonction filtrer avec elles, on ne
calcule donc pas vraiment une moyenne. D'ailleurs, le tableau qui en résulte peut contenir des valeurs négatives. Il ne peut donc
pas être interprété comme étant une image, du moins pas directement.
Soient les lignes de programmes suivantes, saisies directement dans la console :
>>> T = imread("ecole-ng.jpeg")
>>> T [94:97,149:152]
array([[117, 174, 251],
[116, 184, 250],
[119, 207, 249]], dtype=uint8)
>>> T[470:473,600:603]
array([[214, 212, 210],
[214, 213, 212],
[213, 213, 211]], dtype=uint8)
>>> Gx = array([[-1, 0, 1],
[-2, 0, 2],
[-1, 0, 1]], dtype=int32)
>>> Px = filtrer(T, Gx)
Q6. Calculer à la main Px[95,150] et Px[471,601]. Que mesurent les valeurs du tableau Px ? Dans quel cas obtient-on
de grandes valeurs ?
Les valeurs calculées par filtrer(T,Gx) n'ont pas de raison de rester comprises entre 0 et 255. Le tableau retourné doit être
du type « float64 ».
Q7. Modifier en conséquence la fonction filtrer en remplaçant la ligne faisant la copie superficielle de la matrice initiale
par la création d’une matrice remplie de zeros, de type « float64 ».
Nous allons maintenant analyser le contenu du tableau Px. On exécutera pour cela les lignes de code suivantes :
Px = filtrer2(pixels, Gx)
plt.figure()
plt.hist(Px.flat, bins=200, range=(-100, 100))
plt.figure()
plt.imshow(pixels, cmap='gray')
plt.figure()
plt.imshow(Px, cmap='gray')
plt.show()
Q8. Que représentent chacune des figures obtenues ? Existe-t-il une corrélation entre elles ?
L’opération de filtrage précédente a mis en évidence les régions où l’intensité varie fortement dans la direction horizontale. Nous
allons maintenant procéder de même, en effectuant le calcul dans la direction verticale
TD – Informatique – PSI
Q9. Créer de même le tableau Py et le représenter par une image en niveau de gris.
L’association des deux tableaux (Px, Py) peut être interprétée comme un vecteur qui indique la direction dans laquelle la variation
d'intensité est maximale pour chaque pixel. La norme de ce vecteur correspond à cette intensité maximale.
Q10. Créer alors un tableau P de même taille de Px (ou Py) dont les composantes seront les normes des vecteurs définis cidessus. Dans quel cas obtient-on de grandes valeurs dans les cellules de ce tableau et le représenter par une image en
niveau de gris.
Nous avons au final trouvé une méthode qui permet de détecter les contours dans une image.
4. Images en couleur
Le codage informatique des couleurs est l'ensemble des conventions
permettant l'affichage ou l'impression par un périphérique
informatique d'une image en couleurs, plutôt qu'en noir et blanc. Le
codage se base sur la synthèse additive trichrome des couleurs. On
s’intéresse alors à une image codée au format RGB (Red-Green-Blue).
Pour cela, on a trois images nommées « canal0.jpeg », « canal1.jpeg »
et « canal2.jpeg » qui sont les trois canaux RGB d’une même image. Les
canaux 0, 1 et 2 représentent respectivement les intensités de rouge,
de vert et de bleu. Chaque combinaison d'intensités correspond à une
couleur particulière.
Q11. Observer la structure de chacune de ces images, après les
avoirs ouvertes et les avoir affichées. En déduire de combien de couleurs différentes on peut coder dans le format RGB. De
quelle couleur sont les manches des pinceaux ?
Une image RGB complète est représentée par un tableau Numpy à trois dimensions (une dimension par canal) :
>>> pixels[2, 5, 0] # Canal 0 du pixel (2, 5)
165
>>> pixels[2, 5] # Les trois canaux du pixel (2, 5)
array([165, 168, 180], dtype=uint8)
>>> pixels[:, :, 2] # Canal 2 complet
array([[180, 179, 178, ..., 161, 160, 159],
[180, 180, 180, ..., 162, 161, 160],
[180, 180, 181, ..., 162, 161, 160],
...,
[162, 163, 164, ..., 100, 100, 101],
[165, 165, 166, ..., 96, 98, 101],
[167, 167, 167, ..., 88, 94, 101]], dtype=uint8)
L’objectif va être de reconstruire l’image à partir des 3 canaux. Pour cela, on va créer un tableau Numpy de taille (h,w,3), où (h,w)
est la taille d’un tableau correspondant à un canal.
Q12. Créer alors un tel tableau, que l’on pourra nommer pixels et l’afficher sur une nouvelle figure. (Quand on utilisera imshow,
il ne faudra bien entendu ne plus spécifier cmap).
Q13. Assembler de manière différente les canaux pour obtenir une image avec des manches de pinceaux jaunes.
5. Photomaton
Soit l’image « mona-lisa.jpeg » contenue dans le dossier Images. L’afficher. Nous allons trouver une méthode pour réaliser la
transformation suivante :
TD – Informatique – PSI
Sachant que l'image de droite est composée de 256×256 pixels et que l'image de gauche a été obtenue à partir de l'image de droite
en déplaçant uniquement les pixels.
Plus précisément :
- les pixels des lignes paires sont dans la partie haute et les pixels des lignes impaires dans la partie basse ;
- les pixels des colonnes paires sont dans la partie gauche et les pixels des colonnes impaires dans la partie droite.
Notons (i,j) les coordonnées d’un pixel dans l’image initiale, et (u,v) les nouvelles coordonnées de ce même pixel dans l’image
transformée.
Q14. Si i=124 et j=91 ,combien valent u et v ?
Q15. Ecrire une fonctions new_coords(i,j) prenant en argument les coordonnées i et j d’un pixel et qui retourne les
nouvelles coordonnées.
Q16. Ecrire la fonction photomaton(pixels) qui effectue la transformation sur le tableau pixels et qui retourne le résultat
dans un nouveau tableau. Afficher la nouvelle image.
Q17. Réitérer cette expérience plusieurs fois de suite et expliquer ce qui se passe.
6. Stéganographie
Le fichier tranquility.png est une photographie d'un jardin dans un temple Zen.
La visualiser et observer sa structure.
Cependant, le fichier contient une seconde image cachée dans la première.
Chaque pixel d'une image RGB est codé sur 3 octects (un par canal). Au lieu d'utiliser les 8 bits pour coder un entier quelconque
entre 0 et 255, seuls les quatre bits de poids forts ont été utilisés pour coder l'image apparente. Les quatre bits de poids faibles
peuvent alors être utilisés pour l'image cachée.
Intéressons par exemple au canal bleu d'un pixel particulier. Supposons que sa valeur soit A=105 dans l'image apparente originale
et C=96 dans l'image cachée originale.
Q18. Écrire les nombres A et C en binaire.
Q19. Si on met à 0 les 4 bits de poids faibles de A, quel nombre obtient-on ?
Q20. Si on remplace ces quatre bits de poids faibles par les quatre bits de poids forts de C, quel nombre B obtient-on ?
C'est ce nombre B qui remplace A dans le fichier « tranquility.png ».
Q21. Faire apparaître l'image cachée dans le fichier « tranquility.png ».
Support :
- Support de cours d’A. Manzanera (ENSTA)
- Sujet de TP de D. Pinsard
- Sujet de D. Prevost
Téléchargement