Tuto Python & Scikit-Image : traitement d’images

Table des matières

Introduction

  1. Premiers pas avec Scikit-image

1.1. Importer une image

1.2. Afficher une image

1.3. Sauvegarder une image

  1. Manipuler une image

2.1. Histogramme des pixels

2.2. Image en nuance de gris

2.3. Image monochrome

2.4. Détection des bords

2.5. Segmentation

2.6. Les filtres

  1. Exercices

3.1. Exercice 1

3.2. Exercice 2

  1. Solution des exercices

4.1. Exercice 1

4.2. Exercice 2

Conclusion

Introduction

d Dans ce tutoriel, nous allons vous initier au traitement d’images en utilisant la bibliothèque Scikit Image. L'objectif n'est pas d'apprendre la bibliothèque en détail, mais d'en apprendre suffisamment afin que vous soyez capable de réaliser quelques manipulations de bases.

Les images sont devenues une source importante des données numériques, d’où la nécessité de les traiter. En améliorant l'image, en extrayant les données et en les analysant, nous sommes capables de traiter les images et les utiliser dans divers domaines tels que l'intelligence artificielle, l'infographie, la recherche médicale et bien d'autres encore.

En raison de sa popularité croissante et de la disponibilité de nombreuses bibliothèques faciles à utiliser, Python est un excellent choix pour le traitement d’images. Il existe de nombreuses bibliothèques de traitement d'images disponibles en Python comme Numpy, OpenCV, Python Image Library (PIL), etc. Ce tutoriel est centré sur Scikit-image. C’est une bibliothèque open source assez simple, destinée à la recherche, à l'éducation et aux applications industrielles. Elle dispose de nombreux algorithmes de segmentation, de manipulation des couleurs, de filtrage, etc.

Mais avant d'aller plus loin, examinons les bases de l'image numérique. Une image, sous forme numérique, est composée de pixels. Ce sont les plus petites unités d'information qui composent une image. Une façon simple de décrire chaque pixel est d'utiliser une combinaison de trois canaux (couleurs), à savoir le rouge, le vert et le bleu. D’autre part, une image en nuance de gris ne contient qu’un seul canal (le gris) couvrant différentes tonalités de noir et de blanc et les pixels sont représentés par une seule valeur entre 0 et 255.

Maintenant vous pouvez commencer! Allons-y. 

1. Premiers pas avec Scikit-image

Avant de commencer, il faut choisir une image de test. Tout d'abord, installez la bibliothèque Scikit Image en utilisant la syntaxe suivante :

Syntaxe:

pip install scikit-image 

Ensuite, importez-la et vérifiez sa version en utilisant l'attribut version:

Syntaxe:

import skimage
skimage.__version__

Ici, nous avons utilisé la version 0.16.2.

1.1. Importer une image

L'étape indispensable à tout traitement est le chargement de l'image. Pour effectuer cette tâche, il faut importer le module io et utiliser la fonction imread() en lui passant le chemin vers le fichier image test.jpg. Cela devrait nous renvoyer un objet numpy.ndarray, que nous allons placer dans la variable image :

Syntaxe:

from skimage import io
image = io.imread('/path/test.jpg')

Vu que l’image importée est sous forme d’un vecteur numpy, nous utiliserons les attributs dtype et shape pour extraire quelques informations. 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:

# informations
print( 'classe :', type(image) )
print( 'type :', image.dtype )
print( 'taille :', image.shape )

Résultat d’exécution:

Remarquez que notre image importée est un vecteur de classe numpy.ndarray contenant des valeurs de type uint8 (entier) de 3 dimensions dont la forme est (hauteur, largeur, canal (Rouge, Vert, Bleu)).

1.2. Afficher une image

Vous pouvez afficher l’image en utilisant la fonction imshow() du module io :

Syntaxe:

io.imshow(image)
io.show()

Résultat d’exécution:

Le script précédant applique les fonctions imshow() et show()imshow() affiche une image et show() affiche les images mises en file d'attente par imshow. Pour mieux comprendre cela, nous allons utiliser le module data de Scikit Image (skimage) qui contient des images pré chargées (images de démonstration standard qui peuvent être utilisées pour effectuer des tests). Le script suivant affiche deux images (astronautet coffee) de ce module :

Syntaxe:

from skimage import data
image_1 = data.astronaut()
image_2 = data.coffee()
io.imshow( image_1 )
# la commande suivante io.show() est obligatoire pour afficher les deux images simultanément
io.show()
io.imshow( image_2 )
io.show()

Résultat d’exécution:

Vous pouvez utiliser la fonction dir() (qui permet de lister le contenu d'un objet) pour afficher toutes les images disponibles dans le module data.

1.3. Sauvegarder une image

Vous pouvez sauvegarder votre image avec la fonction imsave() du module io. La fonction imsave() prend l'emplacement et l'image qui doit être sauvegardée en tant que paramètres. Le format du fichier sauvegardé est automatiquement déterminé à partir de l'extension :

Syntaxe:

io.imsave('path/nom.png', image) 

2. Manipuler une image

Passons à la pratique ! L'objectif est d'apprendre à manipuler les images en utilisant la bibliothèque Scikit Image et de comprendre les notions basiques avec un exemple concret. Pour cela, Nous allons utiliser notre image de test :

Syntaxe:

io.imshow( image )
io.show()

Résultat d’exécution:

2.1. Histogramme des pixels

Nous affichons l'histogramme de notre image qui représente la répartition des pixels selon leur intensité (de 0 à 255). Les trois canaux (rouge, vert et bleu) ont chacun un histogramme correspondant. Pour créer l’histogramme de notre image test, nous allons utiliser la fonction histogram() du module exposure. Pour le tracer, nous utilisons la bibliothèque matplotlib (assurez-vous que la bibliothèque matplotlib est installée). Le script suivant affiche l’histogramme des pixels de notre image test pour chaque canal :

Syntaxe:

from skimage import exposure
import matplotlib.pyplot as plt
fig, axs = plt.subplots(nrows = 1, ncols = 3, figsize = (13,4))
canals = ['rouge', 'vert', 'bleu']
for i, canal in enumerate(canals):
hist = exposure.histogram( image[ : , : , (i)] )
axs[i].plot(hist[0])
axs[i].set_title(canal)

Résultat d’exécution:

2.2. Image en nuance de gris

Une image couleur (mode RGB) peut être convertie en nuance de gris. Une image en nuances de gris n'a pas de canal puisqu'elle ne donne aucune information sur les couleurs, ainsi la complexité du calcul est réduite lorsqu'elle est utilisée. Pour convertir une image couleur en une image en nuances de gris, utilisez la fonction rgb2grey() du module color qui prend l'image originale en paramètres. Le script est le suivant :

Syntaxe:

from skimage import color
grey_image = color.rgb2grey( image )
io.imshow( grey_image )
io.show()

Résultat d’exécution:

Remarque : L’image en nuance de gris générée à partir de la fonction rgb2grey() a des valeurs de pixels normalisées (entre 0 et 1). Il faut multiplier par 255 pour avoir des valeurs entre 0 et 255.

2.3. Image monochrome

En mode RGB, chaque pixel de l'image est représenté par trois entiers. Pour générer des images monochromes, vous pouvez utiliser une image en nuances de gris et générer la couleur de votre choix, simplement en éliminant les autres couleurs. Nous utilisons la fonction grey2rgb() du module color qui permet de créer une image en mode RGB à partir d’une image en nuances de gris et ensuite appliquer une couleur de notre choix (dans le script suivant nous utiliserons la couleur rouge et violète). Voici comment procéder :

Syntaxe:

color_image = color.grey2rgb( grey_image )
Red = [1, 0, 0]
Violet = [1, 0, 1]
io.imshow(Red * color_image)
io.show()
io.imshow(Violet * color_image)
io.show()

Résultat d’exécution:

2.4. Détection des bords

Pour détecter les bords dans une image, vous pouvez utiliser la fonction canny() du module feature qui implémente le détecteur de bords Canny qui permet de prendre l'image en nuances de gris et une autre valeur (sigma) en paramètres. Le script suivant implémente le détecteur de bords Canny avec sigma = 3 et affiche le résultat :

Syntaxe:

from skimage import feature
edges_image = feature.canny( grey_image, sigma = 3)
io.imshow( edges_image )
io.show()

Résultat d’exécution:

Le traitement d’images joue un rôle essentiel dans la détection des objets. Comme vous pouvez le voir, la partie de l'image qui contient des objets a été pointillée grâce à la détection des bords.

2.5. Segmentation

Le but de la segmentation des images est d'étiqueter chaque pixel d'une image avec une classe correspondante. Il y a plusieurs façons de faire cela, la plus simple consiste à segmenter l’image en deux régions (premier plan et arrière plan). 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. Pour réaliser une segmentation binaire avec un seuil = 180, le script est le suivant :

Syntaxe:

seuil = 180
binary_image = grey_image*255 > seuil
io.imshow( binary_image )
io.show()

Résultat d’exécution:

Comme vous pouvez le voir, dans l’image résultante, deux régions ont été établies : la région noire (valeur de pixel 0) et la région blanche (valeur de pixel 255).

Le module filters du la bibliothèque skimage contient plusieurs algorithmes qui permettent de choisir le seuil de la segmentation. Dans un premier temps, appelons tous les algorithmes de seuillage en utilisant la fonction try_all_threshold. Leur application sur notre image de test génère plusieurs images segmentées :

Syntaxe:

from skimage import filters
filters.try_all_threshold( grey_image )
io.show()

Résultat d’exécution:

Nous pouvons aussi utiliser un seul algorithme à la fois. Dans le script suivant, nous utilisons la fonction threshold_minimum() du module filters pour générer le seuil de la segmentation :

Syntaxe:

seuil = filters.threshold_minimum( grey_image )
binary_image = grey_image > seuil
io.imshow( binary_image )
io.show()

Résultat d’exécution:

2.6. Les filtres

Le filtrage constitue un volet important en traitement d'images. Dans cette partie, nous allons générer des filtres à partir du module filters, créer des images filtrées et comparer les histogrammes des deux images (originale et filtrée).

Syntaxe:

median_image = filters.median( image )
io.imshow( median_image )
io.show()

Résultat d’exécution:

Syntaxe:

# import matplotlib.pyplot as plt
# from skimage.exposure import histogram
fig, axs = plt.subplots(nrows = 1, ncols = 3, figsize = (13,4))
canals = ['rouge', 'vert', 'bleu']
for i, canal in enumerate(canals):
hist1 = histogram( image[ : , : , (i)] )
hist2 = histogram( median_image[ : , : , (i)] )
axs[i].plot( hist1[0], label = 'image originale' )
axs[i].plot( hist2[0], label = 'filre median' )
if i == 0:
axs[i].legend()
axs[i].set_title(canal)

Résultat d’exécution:

Syntaxe:

gaussian_image = filters.gaussian( image )
io.imshow( gaussian_image )
io.show()

Résultat d’exécution:

Syntaxe:

fig, axs = plt.subplots(nrows = 1, ncols = 3, figsize = (13,4))
canals = ['rouge', 'vert', 'bleu']
for i, canal in enumerate(canals):
hist1 = histogram( image[ : , : , (i)] )
hist2 = histogram( gaussian_image[ : , : , (i)] )
axs[i].plot( hist1[0], label = 'image originale')
axs[i].plot( hist2[0], label = 'filre gaussian')
if i == 0:
axs[i].legend()
axs[i].set_title(canal)

Résultat d’exécution:

3. Exercices

3.1. Exercice 1

Dans cet exercice nous utilisons l’image coins du module data :

Syntaxe:

from skimage import data
image_coins = data.coins()
io.imshow( image_coins )
io.show()

Résultat d’exécution:

Question : Tracez un contour sur toutes les pièces et affichez l’image. Vous pouvez utiliser la fonction mark_boundaries du module segmentation.

3.2. Exercice 2

Dans cet exercice nous utilisons l’image logo du module data :

Syntaxe:

from skimage import data
image_logo = data.logo()
io.imshow( image_logo )
io.show()

Résultat d’exécution:

Question 1: Transformez l’image en nuance de gris.

Question 2: Générez une nouvelle image qui ne contient que le serpent (segmentation).

Question 3: Générez une image monochrome de couleur jaune à partir de l’image précédente.

4. Solution des exercices

4.1. Exercice 1

Syntaxe:

from skimage import data
from skimage import feature
from skimage import segmentation
image_coins = data.coins()
image_edges = feature.canny(image_coins, sigma = 4)
image = segmentation.mark_boundaries(image_coins, image_edges)
io.imshow(image)
io.show()

Résultat d’exécution:

4.2. Exercice 2

Question 1:

Syntaxe:

from skimage import color
from skimage import data
image_logo = data.logo()
grey_image = color.rgb2grey( image_logo )
io.imshow( grey_image )
io.show()

Résultat d’exécution:

Question 2:

Syntaxe:

from ipywidgets import interact
def Seg(seuil):
binary_image = grey_image*255 > seuil
io.imshow( binary_image )
io.show()
interact(Seg, seuil = (0,255,1))

Résultat d’exécution:

Question 3:

Syntaxe:

binary_image = grey_image*255 > 160
binary_image = binary_image*255
color_image = color.grey2rgb(binary_image)
Jaune = [1, 1, 0]
io.imshow(Jaune * color_image)
io.show()

Résultat d’exécution:

Conclusion

Nous sommes arrivés à la fin de ce tutoriel. J’espère qu’il vous sera utile pour vous initier au traitement d’images avec la bibliothèque Scikit Image. Cependant, il existe d'innombrables applications de Scikit Imagedans le traitement d’images et de nombreuses fonctions plus avancées qui, combinées à d'autres bibliothèques comme NumPy et OpenCV, donneraient des résultats étonnants. Ceci dit, il y a encore des notions à apprendre. Merci d’avoir lu et bon courage pour la suite.

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