Vincent GODARD - V6 - 06/04/2023
Inspiré de :
Sources :
données Landsat Thematic Mapper (TM) du 10 septembre 1987, ouest de Worcester, Massachusetts (issues du https://clarklabs.org/download/)
scikit-image https://scikit-image.org/
matplotlib https://matplotlib.org/
Dossier compressé à télécharger => ici.
Créez dans le répertoire TD13 un sous répertoire "data" où vous déposerez (bouton Upload Files), depuis votre zone de stockage, les fichiers :
○ "how87tm432.jpg" ;
○ "how87tm321_PS.jpg"
Ce noteboock et le répertoire "data" seront au même niveau dans l'arborescence, tous les deux sous TD13.
Dans le JupyterLab la bibliothèque Matplotlib est déjà installée. Si la bibliothèque skimage ne l'est pas, il faut l'installer.
Il faut ensuite les charger (fonction import).
## Si vous n'avez jamais installé skimage sur votre ordinateur, il est probablement absent des bibliothèques par défaut.
## Pour l'installer, effacez le # (avant le "!").
#!pip install scikit-image
## Import des bibliothèques
import skimage # Bibliothèque de traitement d'images
import skimage.io as io # Utilitaire permettant de lire et d'écrire des images dans différents formats (raccourci en io).
import skimage.metrics # Utilitaire permettant des mesures entre images.
import matplotlib.pyplot as plt # Bibliothèque destinée à tracer et visualiser des données sous forme de graphiques.
## Pour en connaître la version de skimage
skimage.__version__ # attention au "." après skimage
Il se peut que l'import de la version de skimage génère un message d'erreur, comme une incompatibilité avec NumPy, par exemple. Une des bibliothèques est trop ancienne par rapport à l'autre. Il faut donc faire une mise à jour.
## SciPy est un projet qui fédère des bibliothèques Python à usage scientifique.
## Scipy utilise les tableaux et matrices du module NumPy (https://fr.wikipedia.org/wiki/SciPy).
## Mise à jour de scipy, effacez le # (avant le "!") si nécessaire.
#!pip install --upgrade scipy
## Ensuite, il faut l'importer
#import scipy
## Puis vérifier que cette nouvelle version est compatible
#scipy.__version__
On pourra avantageusement regarder à quoi servent les bibliothèques https://scikit-learn.org/stable/ ou https://scikit-image.org/ dont il existe parfois des Wiki en français https://fr.wikipedia.org/wiki/Scikit-learn ou https://fr.wikipedia.org/wiki/Scikit-image.
Lecture d'un fichier image centré sur le secteur des Howe Hill, à l'ouest de Worcester dans le Massachusetts (USA). C'est une composition colorée en couleurs naturelles issue des bandes TM3 (bande du rouge), TM2 (bande du vert) et TM1 (bande du bleu) du Landsat Thematic Mapper (TM) du 10 septembre 1987.
TM3 (bande du rouge) sera codée en rouge, TM2 (bande du vert) sera codée en vert et TM1 (bande du bleu) sera codée en bleu.
## Lecture avec la fonction imread() avant affichage
HOW87TM321 = io.imread('data/how87tm321_PS.jpg')
La dynamique de l'image a été préalablement améliorée pour qu'elle ne soit pas trop terne lors de ce premier affichage, ici avec Photoshop (d'où le _PS). On verra par la suite comment le faire en Python.
## Affichage du nb de lignes, de colonnes et du nb de canaux (le cas échéant)
## Utilisation de la fonction .shape du module .io
print(HOW87TM321.shape)
# Si on souhaite habiller la sortie (texte entre '', puis "," avant la fonction .shape sur la variable HOW87TM123)
print('(nb lignes, nb colonnes, nb de canaux) :', HOW87TM321.shape)
On peut aussi vouloir connaître la classe de notre variable et son type.
○ Les classes sont un moyen de réunir des données et des fonctionnalités : https://docs.python.org/fr/3/tutorial/classes.html
○ Le type d'un objet Python détermine quel genre d'objet c'est : https://docs.python.org/fr/3/glossary.html#term-type
## Affichage des informations
print( 'classe :', type(HOW87TM321) )
print( 'type :', HOW87TM321.dtype )
La classe est un vecteur numpy.ndarray placée dans la variable image HOW87TM321.
Comme il comporte 3 canaux (cf .shape), c'est un tableau à 3 dimensions, c'est donc un 3D array de type NumPy.
Le type est un entier (unsigned integer 8bits) codé sur un octet (256 niveaux de gris par exemple).
Donc, notre image HOW87TM321 est un vecteur de classe numpy.ndarray contenant des valeurs de type uint8 (entier sur un octet) de 3 dimensions dont la forme est : nb lignes, nb colonnes, nb de canaux codés en Rouge, Vert, Bleu.
## Affichage de l’image en composition colorée HOW87TM321, en utilisant la fonction imshow() du module io :
io.imshow(HOW87TM321)
On constate que la fonction imshow() du module io fait appel à matplotlib !
Affichage par itérations (Loop) en utilisant le nb de canaux (cf. Contenu en 2.2).
## Décomposition en 3 canaux RVB affichés en couleurs standards
## Avec une boucle qui parcourt un nombre spécifié de fois (3) l'étendue (range) des canaux "c" en partant du premier
## Attention aux ":" à la fin de la boucle for.
for c in range(3):
plt.imshow(HOW87TM321[:,:,c]) # [:,:,c] permettrait de découper des sous fenêtre (cf. TELED/TD12)
plt.show() # affiche les images mises en file d'attente par imshow() la ligne d'avant.
## Pour voir des exemples de boucles 'for' => https://waytolearnx.com/2019/05/boucle-for-en-python.html?utm_content=cmp-true
À l'œil, la différence n'est pas flagrante ! Cela s'améliorera avec l'image en fausses couleurs (cf. infra chap. 4.) !
## Décomposition en 3 canaux RVB affichés en gris et centrés sur une sous zone
for c in range(3):
plt.imshow(HOW87TM321[400:480,200:300,c], 'gray') # imagettes centrées sur l'aéroport (coord. approx. lues en 3.1)
plt.show()
À l'œil, la différence en niveaux de gris n'est toujours pas flagrante !
Enregistrons ces 3 canaux et comparons-les.
Si un seul canal vous intéresse, pas la peine de faire une boucle.
Rappel : les canaux sont comptés à partir de 0 !
0 = canal 3 (bande du rouge), 1 = canal 2 (bande du vert) et 2 = canal 1 (bande du bleu)
## Sélection du canal à enregistrer, ici le 3.
io.imsave('data/HOW87TM3.png', HOW87TM321[:,:,0])
Si vous souhaitez enregistrer les trois canaux, il vaut mieux recourir à une boucle.
## Création d'un n° de bande équivalent à l'incrément des 'range'
## On crée un tuple, une liste, où deux informations sont liées et non permutables.
img_index_tuple_list = [(0,'TM3'),(1,'TM2'),(2,'TM1')]
## Sélection successive des canaux à enregistrer avec affectation du n° de bande
for c in img_index_tuple_list:
out_img_name = f'data/HOW87{c[1]}.png' # On lui précise où démarrer [1], dans le tuple
io.imsave(out_img_name, HOW87TM321[:,:,c[0]]) # étape d'enregistrement par incrément successif
print(out_img_name) # pour voir ci-dessous quels seront les noms des fichiers dans /data
Il semblerait qu'il n'y ait pas une grosse différence "visuelle" entre les trois fichiers .png créés. Il serait préférable de contrôler leurs différences en comparant leurs histogrammes, par exemple. Ce sera l'objet du prochain TD. En attendant, vous pouvez tester ce que vous venez de faire sur une nouvelle composition colorée, plus contrastée. Une composition colorée dite "fausses couleurs".
Comme nous l'avons vu au TD 12, nous pouvons comparer les canaux deux à deux en utilisant le module de l'erreur quadratique moyenne (MSE).
Chargement des images en mémoire.
## Lecture des images avec la fonction imread() avant affichage
HOW87TM3 = io.imread('data/how87tm3.png')
HOW87TM2 = io.imread('data/how87tm2.png')
HOW87TM1 = io.imread('data/how87tm1.png')
On peut s'assurer que les images font la même taille. C'est une condition pour les comparaisons deux à deux du paragraphe suivant (calcul de la MSE).
## Affichage du nb de lignes, de colonnes et du nb de canaux (le cas échéant)
## Utilisation de la fonction .shape du module .io
print(HOW87TM3.shape)
print(HOW87TM2.shape)
print(HOW87TM1.shape)
## Calcul de l'erreur quadratique moyenne (MSE) entre les deux canaux TM3 et TM2
mse = skimage.metrics.mean_squared_error(HOW87TM3, HOW87TM2) # calcul de l'écart entre les deux canaux avec skimage.metrics via MSE
MSE = f'{mse:.2f}' # Valeur de la MSE avec deux décimales, grandeur sans unité (luminances apparentes ou comptes numériques)
## Affichage de la MSE
print('Valeur de la MSE (erreur quadratique moyenne) entre les canaux TM3 et TM2 :', MSE)
Bien que la différence de radiométries ne soit pas nette à l'œil, la valeur de MSE est sans appel. Les canaux sont différents.
On peut encore le tester entre TM3 et TM1.
## Calcul de l'erreur quadratique moyenne (MSE) entre les deux canaux TM3 et TM1
mse = skimage.metrics.mean_squared_error(HOW87TM3, HOW87TM1) # calcul de l'écart entre les deux canaux avec skimage.metrics via MSE
MSE = f'{mse:.2f}' # Valeur de la MSE avec deux décimales, grandeur sans unité (luminances apparentes ou comptes numériques)
## Affichage de la MSE
print('Valeur de la MSE (erreur quadratique moyenne) entre les canaux TM3 et TM1 :', MSE)
Les canaux TM3 et TM1 sont plus proches que les canaux TM3 et TM2 car leur MSE est plus faible.
Sauriez-vous calculer la MSE entre les canaux TM1 et TM2 ?
Sauriez-vous afficher une autre CC, la HOW87TM432 ? La décomposer en trois bandes ?
Les différences seront plus visibles sur cette composition colorée "fausses couleurs" !