Tuto Python & Mahotas : traitement d'images

Table des matières

Introduction

  1. Premiers pas avec Mahotas
  2. Manipuler une image

2.1. Image en nuances de gris

2.2. Détection des bords

2.3. Segmentation

2.4. Les filtres

2.5. Produit de convolution

2.6. Labellisation

  1. Exercices

3.1. Exercice 1

3.2. Exercice 2

  1. Solution des exercices

4.1. Exercice 1

4.2. Exercice 2

Conclusion

Introduction

Le traitement d’images consiste à manipuler une image dans le but de l’améliorer ou d’en extraire des informations utiles. Chaque image, sous forme numérique, est composée de pixels. Ce sont les plus petites unités d'information qui composent une image.

Mahotas est une bibliothèque de vision par ordinateur et de traitement d'images pour Python qui dispose de plusieurs fonctions (produit de convolution, détection des bords, segmentation, opérateurs morphologiques…) vous permettant d'effectuer de nombreuses actions sans avoir à écrire des centaines de lignes de code. Elle est implémentée en C++, ce qui la rend plus rapide . Cependant, la bibliothèque mahotas a été construite pour être indépendante par rapport à d’autres modules. Ce qui signifie que pour la plupart des utilisations, seule l'importation de mahotas est nécessaire pour faire manipuler ses fonctions.

Dans ce tutoriel, nous allons vous initier au traitement d’images en utilisant mahotas. Nous allons explorer différentes fonctions et méthodes qui existent dans la bibliothèque mahotas pour réaliser quelques traitements d’images basiques.

Maintenant vous pouvez commencer! Allons-y. 

1. Premiers pas avec Mahotas

Commençons par installer la bibliothèque en utilisant la commande pip :

Syntaxe:

pip install mahotas

Vérifiez sa version en utilisant l'attribut version:

Syntaxe:

import mahotas
mahotas.__version__

Dans ce tutoriel, nous avons utilisé la version 1.04.11.

Importons ensuite les bibliothèques avec lequelles nous allons travailler. Importez mahotasmatplotlib.pyplot (pour afficher les images) et la bibliothèque numpy. Vous pouvez aller voir le tutoriel « Les bases de traitement d'images en Python : Bibliothèque NumPy », pour vous initier au traitement d’images avec NumPy.

Syntaxe:

import mahotas
import matplotlib.pyplot as plt
import numpy as np

Pour charger une image à partir d’un fichier avec la bibliothèque mahotas, utilisez la fonction imread(). Dans ce tutoriel, nous utiliserons une image téléchargée à partir de pixabay. Nous appelleronsmahotas.imread() et nous lui passerons le chemin vers le fichier image image.jpg puis nous affichons quelques informations sur l’image importée :

Syntaxe:

image = mahotas.imread('/path/image.jpg')
# quelques informations sur l'image importée
print( 'classe :', type(image) )
print( 'type :', image.dtype )
print( 'taille :', image.shape )
print('valeur minimale :', image.min())
print('valeur maximale :', image.max())
print('valeur moyenne :', np.round(image.mean() , 2) )

Résultat d’exécution:

Remarquez que la fonction imread() génère un vecteur de classe numpy.ndarray contenant des valeurs de type uint8 (entier) de 3 dimensions. Affichons maintenant l’image avec matplotlib 

Syntaxe:

plt.imshow( image )
plt.show()

Résultat d’exécution:

Nous pouvons maintenant commencer à manipuler l’image. Le script suivant transforme notre image en utilisant la fonction rgb2sepia() du module colors :

Syntaxe:

from mahotas import colors
sepia_image = colors.rgb2sepia( image )
plt.imshow( sepia_image )
plt.show()

Résultat d’exécution:

Finalement, vous pouvez enregistrer un vecteur numpy sous forme de fichier image avec la méthode imsave() de mahotas. Le format de l’image sauvegardé est automatiquement déterminé à partir de l'extension :

Syntaxe:

mahotas.imsave('/path/sepia_image.jpg', sepia_image)

2. Manipuler une image

Nous avons importé toutes les bibliothèques dont nous avons besoin, maintenant, passons à la pratique ! L'objectif de cette partie est de réaliser quelques traitements d’images en utilisant la bibliothèque mahotas et de comprendre des notions basiques avec des exemples concrets.

2.1.  Image en nuances de gris

Pour convertir une image couleur en une image en nuances de gris, utilisez la fonction rgb2grey() du module colors qui prend l'image originale en paramètres. Le script est le suivant :

Syntaxe:

# image en nuance de gris
from mahotas import colors
grey_image = colors.rgb2grey(image)
# affichage
plt.imshow( grey_image, cmap = 'Greys_r')
plt.show()

Résultat d’exécution:

Affichons quelques informations sur l’image en nuances de gris :

Syntaxe:

# informations sur l’image en nuance de gris
print( 'classe :', type(grey_image) )
print( 'type :', grey_image.dtype )
print( 'taille :', grey_image.shape )
print('min :', grey_image.min())
print('max :', np.round(grey_image.max(), 2) )

Résultat d’exécution:

Remarquez que le type des valeurs est float64. Pour pouvoir la manipuler par la suite, il est conseillé de transformer le type de valeurs en uint8 :

Syntaxe:

grey_image = np.uint8( grey_image )

2.2.  Détection des bords

Pour détecter les bords dans une image, vous pouvez utiliser la fonction sobel() de mahotas qui implémente le détecteur de bords sobel en prenant l'image en nuances de gris en paramètres. Le script suivant implémente le détecteur de bords sobel et affiche le résultat :

Syntaxe:

from mahotas import sobel
img = grey_image.astype('int8')
# détecteur de bords sobel
edges_image = sobel(img, just_filter = True)
# affichage
plt.imshow(edges_image, cmap = 'Greys_r')
plt.show()

Résultat d’exécution:

2.3. Segmentation

Le but de la segmentation des images est d'étiqueter chaque pixel d'une image avec une classe correspondante. Il existe plusieurs façons de faire cela, la plus simple consiste à segmenter l’image en deux régions (image binaire). Cela revient à convertir l'image en nuances de gris, et fixer un seuil. Les pixels dont la valeur est supérieure au seuil sont traités comme appartenant à la première région, et les autres pixels (inferieur au seuil) à la deuxième région. Le module thresholding de la bibliothèque mahotas contient plusieurs algorithmes de segmentation. Les deux fonctions otsu et Riddler-Calvard (rc) permettent de générer un seuil de segmentation. D’autre part, la fonction bernsen permet de réaliser une segmentation plus générale.

Dans le script suivant, nous générons un seuil avec la fonction otsu() du module thresholding puis nous affichons l’image binaire (segmentée) :

Syntaxe:

from mahotas import thresholding
# segmentation otsu (seuil)
seuil_otsu = thresholding.otsu( grey_image )
print('seuil de segmentation :', seuil_otsu)
# image booléen
otsu_image = grey_image > seuil_otsu
# affichage
plt.imshow(otsu_image, cmap = 'Greys_r')
plt.show()

Résultat d’exécution:

Dans le script suivant, nous générons un seuil avec la fonction rc() du module thresholding puis nous affichons l’image binaire (segmentée) :

Syntaxe:

# segmentation Riddler-Calvard
seuil_rc = thresholding.rc( grey_image )
print('seuil de segmentation :', seuil_rc)
# image booléen
rc_image = grey_image > seuil_rc
# affichage
plt.imshow(rc_image, cmap = 'Greys_r')
plt.show()

Résultat d’exécution:

Remarquez que les deux fonctions renvoient à peu près le même seuil de segmentation, ce qui génère la même image binaire. Finalement, le script suivant génère une image segmentée avec la fonction bernsen() et affiche le résultat :

Syntaxe:

# segmentation bernsen
bernsen_image = thresholding.bernsen(grey_image, 7, 200)
plt.imshow(bernsen_image, cmap = 'Greys_r')
plt.show()

Résultat d’exécution:

2.4.  Les filtres

Le filtrage constitue un volet très important en traitement d'images. Dans cette partie, nous allons générer des filtres (gaussien et médian) en utilisant la bibliothèque mahotas. Les filtres gaussien et médian sont largement utilisés en traitement d'images car ils permettent sous certaines conditions de réduire le bruit.

Pour générer une image avec le filtre gaussien, utilisez la fonction gaussian_filter(). Cette fonction prend l’image originale en paramètres et génère une image filtrée. Le script suivant applique le filtre gaussien à notre image de test, transforme le type des valeurs en uint8 et affiche l’image filtrée :

Syntaxe:

# filtre gaussien
gaussian_image = mahotas.gaussian_filter(image, 2)
gaussian_image = np.uint8( gaussian_image )
# affichage
plt.imshow( gaussian_image )
plt.show()

Résultat d’exécution:

Pour générer une image avec le filtre médian, utilisez la fonction median_filter() de mahotas. Cette fonction prend l’image originale en paramètres et génère une image filtrée. Le script suivant applique le filtre médian à l’image de test, transforme le type des valeurs en uint8 et affiche l’image filtrée :

Syntaxe:

# filtre médian
median_image = mahotas.median_filter(image)
median_image = np.uint8( median_image )
# affichage
plt.imshow( median_image )
plt.show()

Résultat d’exécution:

2.5. Produit de convolution 

Dans cette partie, nous verrons comment nous pouvons effectuer un produit de convolution de l'image en utilisant mahotas. La convolution est une opération mathématique linéaire qui permet de modifier chaque pixel de l'image en fonction des valeurs des pixels de son voisinage en précisant le filtre (masque ou kernel) de convolution. Le script suivant génère un masque de convolution de taille 3x3, effectue le produit de convolution en utilisant la fonction convolve() et affiche le résultat :

Syntaxe:

# filter (masque)
print('filtre :')
filter = np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1]])
print(filter)
# produit de convolution
conv_image = mahotas.convolve(grey_image, filter)
# affichage
print("Image après convolution :")
plt.imshow(conv_image, cmap = 'Greys_r')
plt.show()

Résultat d’exécution:

2.6.  Labellisation

Les images labélisées sont des images dont les valeurs correspondent à différentes régions. Par exemple, la première région correspond à tous les pixels qui ont la valeur x1, la région 2 correspond aux pixels qui ont la valeur x2, et ainsi de suite. Par convention, la région 0 est l'arrière-plan, cette dernière est souvent traitée différemment.

Pour éclaircir cela avec un exemple, la première étape consiste à créer une image binaire:

Syntaxe:

# image binaire
binary_image = np.zeros((10, 10))
binary_image[ 0 : 1 , 0 : 1 ] = 255
binary_image[ 4 : 6 , 0 : 2 ] = 255
binary_image[ 6 : , 6 : ] = 255
binary_image[ 3 : 4 , 3 : 4 ] = 255
binary_image[ 3 : 4 , 6 : 8 ] = 255
binary_image[ 7 : 8 , 2 : 4 ] = 255
# affichage
plt.imshow( binary_image, cmap = 'Greys_r')
plt.show()

Résultat d’exécution:

Pour détecter les différentes régions dans l’image, nous utilisons la fonction label() qui prend l’image originale en paramètre et renvoie une image labélisée et le nombre de régions détectées.

Syntaxe:

# régions (fonction label)
label_image, objects = mahotas.label(binary_image)
print('nombre de régions :', objects)
# affichage
print("nouvelle image :")
plt.imshow(label_image, cmap = 'Greys_r')
plt.show()

Résultat d’exécution:

Nous pouvons filtrer l’image précédente en ne gardant que les régions de taille supérieure à 2. C'est-à-dire les régions qui ont un nombre de pixels supérieur à 2. Pour réaliser cette tâche, nous utilisons les fonctionslabeled_size() et remove_régions() du module labeled :

Syntaxe:

from mahotas import labeled
# filtrage en se bansant sur la taille des régions
sizes = labeled.labeled_size(label_image)
too_small = np.where(sizes label_image = labeled.remove_regions(label_image, too_small)
# affichage
plt.imshow(label_image, cmap = 'Greys_r')
plt.show()

Résultat d’exécution:

Nous pouvons aussi éliminer les régions qui se trouvent au bord de l’image en utilisant la fonction remove_bordering() du module labeled :

Syntaxe:

# élimination des régions sur les bords de l'image
label_image = labeled.remove_bordering(label_image)
plt.imshow(label_image, cmap = 'Greys_r')
plt.show()

Résultat d’exécution:

3. Exercices

Pour les exercices, nous allons utiliser des images générées à partir du module demos.

Syntaxe:

from mahotas import demos

3.1. Exercice 1 

Dans cet exercice, nous allons utiliser l’image suivante :

Syntaxe:

image = demos.load('luispedro', as_grey = True)
plt.imshow(image, cmap = 'Greys_r')
plt.show()

Résultat d’exécution:

Question : (Détection des bords) Utilisez le produit de convolution pour détecter les bords de l’image précédente. Utilisez le filtre (masque) suivant :

Syntaxe:

# filtre (masque)
filter = np.array( [[0, -1, 0], [-1, 4, -1], [0, -1, 0]] )

3.2. Exercice 2 

Dans cet exercice, nous allons utiliser l’image suivante :

Syntaxe:

img = demos.load('nuclear')
img = img[ : , : , 2]
plt.imshow(img)
plt.show()

Résultat d’exécution:

Question 1: Appliquez le filtre gaussien à l’image précédente (img), puis effectuez une segmentation binaire en utilisant un seuil = la moyenne et affichez le résultat.

Question 2: Utilisez la fonction label() pour détecter les différentes régions (cellules). Affichez le nombre de régions détectées et l’image résultante.

Question 3: Éliminez les régions qui ont une taille supérieure à 10000. Affichez le résultat.

Question 4: Éliminez les régions qui sont à la bordure de l’image. Affichez le résultat.

4. Solution des exercices

4.1. Exercice 1 

Syntaxe:

# import numpy as np
# import mahotas
# import matplotlib.pyplot as plt
# image originale
print('image originale :')
plt.imshow(image, cmap = 'Greys_r')
plt.show()
# filtre (kernel)
print('filtre :')
filter = np.array( [[0, -1, 0], [-1, 4, -1], [0, -1, 0]] )
print(filter)
# produit de convolution
conv_image = mahotas.convolve(image, filter)
print("image après convolution :")
plt.imshow(conv_image, cmap = 'Greys_r')
plt.show()

Résultat d’exécution:

4.2. Exercice 2 

Question 1:

Syntaxe:

# import mahotas
# import numpy as np
# import matplotlib.pyplot as plt
# filtre gaussien
img = mahotas.gaussian_filter(img, 4)
# segmentation
seuil = img.mean()
print('seuil de segmentation :', np.round(seuil, 2) )
# image booléen
img = img > seuil
# affichage
plt.imshow(img)
plt.show()

Résultat d’exécution:

Question 2:

Syntaxe:

label_image, objects = mahotas.label(img)
print('nombre de régions :', objects)
plt.imshow(label_image)
plt.show()

Résultat d’exécution:

Question 3:

Syntaxe:

# éliminations des cellules de taille supérieur à 10000
from mahotas import labeled
sizes = labeled.labeled_size( label_image )
too_big = np.where(sizes > 10000)
label_image = labeled.remove_regions(label_image, too_big)
plt.imshow(label_image)
plt.show()

Résultat d’exécution:

Question 4:

Syntaxe:

# élimination des cellule sur les bords de l'image
label_image = labeled.remove_bordering(label_image)
plt.imshow(label_image)
plt.show()

Résultat d’exécution:

Conclusion

Nous sommes arrivés à la fin de ce tutoriel. J’espère qu’il vous sera utile à vous initier au traitement d’images avec mahotas et que vous avez apprécié la facilité de réaliser des manipulations d’images avec cette bibliothèque. Cependant, il existe de nombreuses autres fonctions que vous pouvez découvrir et utiliser par vous-même, n’arrêtez pas ici et continuez à apprendre. Merci d’avoir lu et bon courage pour la suite.

Article publié le 13 Décembre 2020par Sami Nadif