Création et utilisation d’un masque circulaire sur Python

Temps de lecture : 4 minutes

Voici ce que nous allons faire ensemble dans cet article : utiliser un masque circulaire pour colorier une image en blanc en excluant un domaine circulaire central. Ok, la phrase est un peu alambiquée mais regarde l’image, tu comprendras 😅.

« Epuration » d’une image en utilisant un masque circulaire

[Code complet (Jupyter Notebook) et ressources sur Github]

Contexte général

Il est courant qu’on veuille appliquer des traitements uniquement sur certaines régions d’une image. Une façon de faire dans Python consiste à masquer les parties non-utiles de ladite image (qu’on dénommera img). Le principe est simple et se décline en 3 grandes étapes :

  1. Créer une nouvelle matrice masque ayant les mêmes dimensions que img
  2. Les pixels de masque correspondant aux endroits à masquer sur img sont remplis de 0 (False) tandis que les endroits d’intérêt (à ne pas masquer) sont remplis de 1 (True)
  3. Pour accéder uniquement à la zone d’intérêt, il suffit désormais d’écrire img [masque]

Ceci est relativement simple si la zone d’intérêt est de forme rectangulaire. Il est moins direct quand celle-ci est circulaire, parce qu’il faut réussir (grossièrement) à créer un masque de la forme :

Idée clef

Pas de panique, il y a une solution ! En connaissant le rayon et le centre du domaine circulaire désiré, on peut utiliser l’équation caractéristique du cercle pour générer le masque adéquat. Au cas où tu l’as oubliée, elle ressemble à ceci :

(x - x_0)^2 + (y - y_0)^2 = r^2

(x,y) sont les coordonnées d’un point quelconque de l’image, (x_0,y_0) les coordonnées du centre du domaine circulaire et r, son rayon. Les pixels à l’intérieur du domaine circulaire respectent donc l’équation :

(x - x_0)^2 + (y - y_0)^2 < r^2

Implémentation

Rappelle-toi, l’image img n’est qu’une matrice avec des lignes allant de i = 0 à i = i_{max} et des colonnes allant de j = 0 à j = j_{max}. Avec cette représentation en tête, on sait donc que quel que soit le point de l’image, sa coordonnée x va de j = 0 à j = j_{max} et sa coordonnée y va de latex] à i = i_{max}.

Récupération des dimensions de l’image

Pour déterminer i_{max} et j_{max}, la librairie numpy propose shape () qui retourne les dimensions de l’image. Pour créer les vecteurs x (allant de 0 à j_{max}) et y (allant 0 à i_{max}) on utilise la méthode arange().

[imax,jmax] = np.shape(img) 
x = np.arange(0,jmax); 
y = np.arange(0,imax); 
Création du masque

Il s’agit désormais à chaque position (x,y) de calculer la valeur de (x – x_0)^2 + (y – y0)^2. Si on le fait pour toutes les combinaisons de (x,y), on doit retrouver à la fin une matrice m de taille (i_{max}, j_{max}). Pour faire ça, on utilisera la propriété formidable de Python qui est que, quand on additionne une matrice ligne avec j_{max} colonnes et une matrice colonne avec i_{max} lignes, on obtient une matrice finale avec i_{max} lignes et j_{max} colonnes. Tu as peut-être deviné le prochain move : transformer x en matrice ligne et y en matrice colonne. Eh bien, bonne nouvelle, tu as raison 😀! On utilisera la méthode de numpy « newaxis ».

x = x[np.newaxis,:]; #transformation en matrice ligne
y = y[:,np.newaxis]; #transformation en matrice colonne

On peut désormais calculer tranquillement notre matrice m :

m = (x - j0)**2 + (y - i0)**2

Cette étape peut paraître un peu confuse surtout avec l’usage de np.newaxis pour obtenir la matrice m mais amuse-toi avec de petites matrices et affiche les résultats intermédiaires, tu verras que c’est plutôt simple et qu’il est juste question de se familiariser avec la représentation des données 😉.

Maintenant qu’on a la matrice m avec les valeurs d’intérêt, rappelle-toi, les pixels qui appartiennent au domaine circulaire central sont ceux qui ont des valeurs de m inférieures à r^2. Dans le masque final, à ces positions on affectera 1 et dans le reste du masque on mettra plutôt des 0. Python permet de créer un tel masque en effectuant magiquement ces 2 opérations en une ligne :

masque = m < r**2

A cette étape, si on affiche notre masque, il doit ressembler à ceci :

Application du masque

Ok, je te l’accorde, le masque n’a pas un style mortel mais c’est ce qu’on va pouvoir en faire qui est cool. On peut en effet l’utiliser pour mettre la région hors domaine centrale (~masque) en blanc par exemple. En une ligne en plus …

# Remplissage de l'extérieur du domaine circulaire en blanc
img[~masque] = np.max(img); # np.max(img) correspond au maximum d'intensité de l'image, en l'occurence du blanc

# Affichage de la nouvelle image et du centre du domaine circulaire
plt.imshow(cp, cmap = 'gray'); 
plt.plot(j0,i0,'ro');
Image finale

Clean, n’est-ce-pas 😀? Ceci n’est qu’une illustration mais cette technique de masque peut avoir des applications plus intéressantes en traitement d’images. Par exemple, on peut s’en servir pour accélérer les temps de traitement en masquant les zones inutiles; ou pour améliorer la qualité de certains traitements en ciblant certaines régions uniquement.

Partager

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *