TP2 – T RAITEMENT D ’ IMAGE :
C AMERA THERMIQUE ET S UIVI DE TRAJECTOIRE
1. P
RELIMINAIRESPour faciliter la suite du projet et afin que tout le monde aie la même trame, voici quelques fonctions qui seront utiles :
import numpy as np form pylab import * def Gris(tabPix):
"""Convertit en niveau de gris"""
tabPix1=tabPix.copy()
tabPix1=tabPix1.astype(int)
tabPix1[:,:,0]=(tabPix1[:,:,0]+tabPix1[:,:,1]+tabPix1[:,:,2])/3#moyenne des 3 couleurs
tabPix1[:,:,0]=tabPix1[:,:,0]
tabPix1[:,:,1]=tabPix1[:,:,0]
tabPix1[:,:,2]=tabPix1[:,:,0]
tabPix1=tabPix1.astype('uint8') return tabPix1
def Seuillage(im,seuil) :
"""retourne un tableau seuillé """
tmp=im.copy()
tmp[:,:,0]=(im[:,:,0]>seuil) tmp[:,:,1]=(im[:,:,1]>seuil) tmp[:,:,2]=(im[:,:,2]>seuil) return tmp*255
Q1. Copier/coller ces deux fonctions au début de votre programme. Expliquer leur role.
Quelques petites remarques sur les tableaux images :
Pour créer une image à partir de rien, on créé une matrice numpy pleine de zéros que l’on remplira ensuite.
tabPix = np.zeros((ligne,colonne,3),dtype=np.int32)
Attention, (ligne,colonne) est à l’envers de (largeur,hauteur).
Il faut que le type de données soit toujours des entiers non signés sur un octet (uint8).
Cependant afin de pouvoir faire du traitement de l’image en toute liberté, le type sera un entier sur 4 octets (int32). Il faudra prendre la précaution de revenir en uint8 avant l’affichage ou la sauvegarde.
tabPix = np.uint8(tabPix)
Pour créer une image, c’est aussi très simple.
NouvImage = im.fromarray(tabPix) ou tabPix.copy()
On peut sauvegarder sur le disque ou visualiser l’image. On enregistre sur le disque à côté du fichier python. On peut utiliser les formats « jpg » ou « png » ou d’autres nouvImage.save("image.jpg")
On visualise avec imshow(tabPix)
Pour la suite, vous pouvez alors importer directement ces fonctions précédentes dans votre fichier en prenant soin de bien vous placer dans votre répertoire de travail avec la commande (certaines versions de Python nécessitent quand même l’exécution du code dans fonctions.py avant de passer à la suite) :
import os
os.chdir ("C :\\votre chemin”)
2. C
AMERA THERMIQUEOn a récupéré l’image d’une maison, issue d’une caméra thermique, nommée « image-thermique.jpg ». On veut faire ressortir les parties les plus chaudes pour repérer d’éventuelles fuites thermiques. On souhaiterait ne garder que les 10% les plus chauds en couleur, le reste passant en niveau de gris.
Pour manipuler plus aisément l’échelle de température, il est préférable de couper l’image initiale qui fait 540*720 pixels en deux images « image-thermique_reduite.jpg » où on supprime toute la partie droite de la photo contenant l’échelle et « echelle_de_temperature.jpg » où on ne garde que le cadre échelle.
Q2. Ecrire la procédure qui permet de créer ces deux images.
Comme les pixels chauds sont rouges voire blancs, on peut effectuer un travail de seuillage sur les différents canaux (R-V-B) : on garde tels quels les pixels dont les canaux appartiennent à un certain intervalle, les autres sont passés en niveau de gris. Il faudra faire attention au violet qui est constitué de rouge et de bleu …
La méthode utilisée pour le niveau de gris tient compte du fait que l’œil est plus sensible au vert et utilise la recommandation ITU-RBT601 pour les écrans 4:3 et 16:9 dans laquelle la couleur RVB est remplacée par Y=0.299∗R+0.587∗V+0.114∗B
Q3. Créer une fonction seuillageThermique(tabPix) et proposer les "bonnes" valeurs de seuil sur l’image «image-thermique_reduite.jpg ». On enregistrera le résultat de sortie dans un fichier nommé «points_chauds.jpg ».
3. F
LOUTAGEOn considère les matrices (
) et (
)
Pour chaque élément bij de B, on considère la matrice 3×3 Bij qui l’entoure, on calcule le produit de convolution de A par Bij (multiplication classique A*Bij) et on note cij la somme des coefficients de la matrice produit obtenue (fonction np.sum).
Si bij est un élément en bordure de B, on posera cij = bij. On forme ainsi une nouvelle matrice C dont les éléments intérieurs sont les cij (et les éléments au bord sont les bij).
On dit qu’on a filtré la matrice B par la matrice A, ou qu’on a appliqué le masque (filtre) A sur l’image B.
Q4. Créer la fonction filtrer1(filtreA,matB) qui prend en argument une matrice carrée filtreA de dimension taille×taille (taille est un entier impair ≥ 3) et une matrice quelconque matB de dimensions supérieures, et qui renvoie la matrice C (attention : si taille>3, la bordure devra être plus épaisse).
y=67
y=470
x=652 x=686
Vous pouvez tester votre fonction avec :
matA=np.array([[1,2,3],[-1,0,1],[-1,1,2]]) matB=np.array([[5,6,7,8,9,10],
[-5,-6,-7,-8,-9,-10], [1,1,1,1,1,1],
[2,2,3,3,4,4], [0,0,1,2,3,3]]) print(filtrer1(matA,matB))
On souhaite appliquer le filtre A aux 3 tableaux B[:,:,0], B[:,:,1], B[:,:,2] et enregistrer le résultat dans une matrice C de même format que B.
Voici la fonction filtrer(filtreA,matB) qui prend en argument une matrice carrée filtreA de dimension taille×taille et un tableau numpy matB de dimensions n×p×3,et qui renvoie le tableau C de dimensions identiques.
def filtrer(filtreA,matB):
taille=filtreA.shape[0] #matrice carree, donc shape[0]=shape[1]
bordure=taille//2
nb_lig,nb_col,nb_coul=matB.shape
matC=np.int32(np.copy(matB)) # forcer le type int32 for i in range(bordure,nb_lig-bordure):
for j in range(bordure,nb_col-bordure):
for k in range(3):
Bij=matB[i-bordure:i+bordure+1,j-bordure:j+bordure+1,k]
matC[i,j,k]=np.sum(filtreA*Bij) return matC
Pour des pixels, on applique le filtre A aux 3 tableaux B[:,:,0], B[:,:,1], B[:,:,2] et enregistrer le résultat dans une matrice C de même format que B.
Ces matrices réalisent un floutage par moyenne simple (coefficients tous égaux, de somme 1). Plus la taille du filtre est grande, plus le flou sera fort. On peut améliorer cette technique en utilisant un flou gaussien. Son principe est de calculer une moyenne pondérée en accordant plus de poids au pixel central et en diminuant le poids des pixels périphériques.
La matrice servant de filtre est calculée selon le modèle d’une courbe de Gauss (courbe en cloche) à 2 dimensions :
La fonction de Laplace-Gauss est ( )
√
A deux dimensions on utilise ( )
√
Plus l’écart type σ est grand, plus l’image sera floutée. En pratique, σ et la taille (impaire) du filtre étant fixés, on calcule chaque élément de la matrice filtre par la formule ( ) , où x et y sont le nombre de lignes et de colonnes qui séparent cet élément du centre, et k un coefficient constant tel que la somme de tous les éléments (de type float) ainsi calculés soit 1.
Par exemple, pour taille=5 et σ=0.9, le filtre est proche de :
(
)
Voici la fonction matriceFlouGaussien(taille,sigma) qui prend en argument la taille (impaire) de la matrice de floutage, sigma l’écart type de déviation standard et qui retourne la matrice filtre correspondant au niveau gaussien.
##resultat
[[ 5 6 7 8 9 10]
[ -5 38 44 50 56 -10]
[ 1 -32 -37 -42 -47 1]
[ 2 9 12 14 14 4]
[ 0 0 1 2 3 3]]
assert taille %2==1 # on verifie que taille est impaire mat = np. zeros ([ taille , taille ])
taille = taille // 2
for x in range (- taille , taille +1) : for y in range (- taille , taille +1) :
mat [x+ taille ,y+ taille ] = np.exp (-(x **2+ y **2) /(2*( sigma **2) )) return mat /np. sum ( mat )
Q5. Copier/coller les deux fonctions précédentes. Ecrire ensuite la fonction FloutageGaussien(tabPix,taille,sigma) qui utilise la fonction matriceFlouGaussien(taille,sigma) et filtrer(filtreA,matB) et qui renvoie la matrice tabPix qui a été floutée grâce au filtre défini dans la fonction matriceFlouGaussien
3. S
UIVI D’
UNE BALLEVous trouverez dans le répertoire image les images issues d’une vidéo où une balle de golf blanche roule sur un plan incliné bleu. Sur la trajectoire de la balle, on trouve un obstacle modifiant ainsi la trajectoire suite au choc.
Une exportation a été faite afin de convertir cette vidéo en une série de 32 images. Cette opération simple a été faite grâce au logiciel « VirtualDub » accompagné du plug-in « DShowInput » (compatibilité de certains codecs). Afin de réduire, en masse, la taille des images, il a été ensuite utilisé « XnView ». Vous pouvez, si besoin, trouver aussi ces logiciels gratuits sur internet.
Nous sélectionnons, parmi ces images, l’image « trajectoire9.jpeg » comme étant pour nous une image représentative de l’ensemble des images à cause du flou de bougé de la balle, de la présence de la main et de la brique. (Le flou de bougé est le flou produit par le mouvement pendant l’exposition. L’exposition désigne la quantité totale de lumière reçue par la surface sensible (ici le capteur numérique) pendant la prise de vue.)
Q6. Charger l’image dans le tableau traj9. Pour la visualiser utiliser pl.imshow() Q7. Passer « l’image » en niveau de gris et afficher l’image.
Q8. Flouter « l’image » sur 9 pixels avec un écart type de 2.
On s’aperçoit que le temps de calcul est très long et cela peut être très préjudiciable pour traiter les 39 images. Nous allons donc définir une fonction optimisée floutageOptimise. Copier/coller la fonction suivante :
def floutageOptimise(tabPix,iteration):
"""floutage carré(moyenne avec les plus proche voisin, le nb d'iteration définie la puissance du floutage"""
flou=tabPix.copy() flou=flou.astype(float) for i in range(iteration):
flou[1:-1,1:-1]=(flou[2:,1:-1]+flou[:-2,1:-1]+flou[1:-1,2:]+flou[1:-1,:-2])/4 return flou.astype('uint8')
balle1flou=floutageOptimise(balle1,20) #il faut que balle1 soit l’objet grisé !!
pl.figure()
pl.imshow(balle1flou)
Q9. Définir une fonction Bordure(tabPix,taille) en convertissant en noir les pixels du bord de l’image, sur un nombre taille de pixels.
La fonction a comme paramètre tabPix un tableau de pixels en niveau de gris et taille la largeur en pixels de la bordure. Elle retourne un tableau de pixels en niveau de gris.
Q10. Convertir en noir le bord non flouté puis afficher l’image.
Q11. Seuiller « l’image » avec un seuil de 130 et afficher l’image.
Q12. Définir une fonction Centre(tabPix) qui retourne un tuple : les coordonnées du centre de la tâche blanche. La fonction a comme paramètre tabPix un tableau de pixels en niveau de gris.
Q13. Donner le centre de la tâche blanche puis tracer le point sur un graphique (Matplotlib.pyplot) en mettant en fond l’image (plt.imshow(np.uint8(tabPix))
Q14. Traiter les 32 images afin d’obtenir la trajectoire de la balle sur un graphique Matplotlib.pyplot avec les 32 centres uniquement. Fusionner ces centres sur un fond blanc ou sur un fond image, en faisant la moyenne sur un pixel des 32 images.
Voici le début de votre programme :
L = os.listdir('votre chemin') #On liste les images final[:,:,:]=255 #option fond blanc
#final=scm.imread(L[0])#option image 1 en arrière plan X=[]
Y=[]
for i in L:
#A vous de jouer : analyse de toutes les images
4. H
ISTOGRAMME(
PARTIE INDEPENDANTE)
Dans un nouveau fichier :
Q15. Charger l’image « trajectoire9.jpg » dans le tableau traj9. Puis passer « l’image » en niveau de gris
Q16. Flouter « l’image » sur 9 pixels avec un écart type de 2. Puis convertir en noir le bord non flouté
Q17. Faire un histogramme du niveau de gris.
A chaque fois que le seuil franchit un pic, on est susceptible d’effacer de l’image un ou des objets. Il peut être parfois utile d’effectuer un seuil juste sur une plage de valeurs.
Q18. Vérifier dans la mesure du possible cette affirmation.
Q19. Faire un choix sur le seuil à appliquer de façon à ne sélectionner que les pixels de la balle.
Q20. Seuiller « l’image » avec le seuil choisi et Afficher l’image.