Sélection manuelle d’une région circulaire sur une image (Python)

Temps de lecture : 4 minutes

Voici ce que je te propose qu’on fasse dans cet article : retrouver le centre et le rayon d’un domaine circulaire sur une image à partir de 3 points choisis par clics sur son périmètre.

Détermination du centre et du rayon d’un domaine circulaire à partir du choix par clics de 3 points sur son périmètre

En traitement d’images, quand on dispose d’une grande image, il peut arriver qu’on ne soit intéressé que par une petite portion de celle-ci. On peut donc vouloir récupérer uniquement cette région pour nos traitements. Si elle est de forme rectangulaire, on détoure tout simplement l’image mais quid si notre fameuse région d’intérêt est de forme circulaire ?

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

Cas d’étude pratique

Dans mes travaux de doctorat où je filmais le déplacement de particules dans un domaine circulaire, certaines parties du banc expérimental étaient dans le champ de la caméra. Ces parties étaient inutiles pour les traitements ultérieurs, tout ce qui m’intéressait c’était les particules dans le domaine circulaire central.

Région d’intérêt de forme circulaire

Maintenant que tu connais si bien le contexte, regardons comment on retrouve les coordonnées de ce domaine circulaire. Ah, je précise au cas où… vu que c’est un cercle, ceci revient simplement à déterminer son centre et son rayon.

Détermination du domaine circulaire

La propriété essentielle qu’on utilisera dans la résolution de ce problème est le fait que si on prend 3 points non alignés, il existe un unique cercle passant par les 3. En d’autres termes, les coordonnées de 3 points sur le périmètre de notre domaine circulaire suffisent à le caractériser entièrement… oui, je sais, c’est génial, la géométrie 😋! Je ne vais pas te démonter ça mathématiquement, mais tu peux dire merci à l’équation caractéristique du cercle et à la résolution d’équations algébriques.

Récupération par clics des coordonnées des points

Bon alors, première étape, trouver les 3 points. On peut simplement les sélectionner en cliquant sur l’image affichée. Python dispose en effet d’une fonction pour récupérer les coordonnées d’un point cliqué sur une image : la fonction ginput de matplotlib.pyplot. En code, la lecture, l’affichage et la récupération des 3 points peuvent se faire comme suit :

#---------IMPORTATION DES LIBRAIRIES---------------------------
%matplotlib qt 
# qt pour afficher des fenêtres flottantes (et non inline)
import matplotlib.pyplot as plt
from skimage import io
import numpy as np
from copy import copy


# Lecture de l'image, as_gray = True permet de la lire en niveaux de gris
img = io.imread(r'.\image.png', as_gray = True)

# Affichage de l'image
plt.imshow(img, cmap = 'gray')

#---------DETERMINATION DU DOMAINE CIRCULAIRE-------------------
# Récupération des coordonnées de 3 points par clic
coord3points = plt.ginput(3, timeout = -1)
# permet de récupérer les coordonnées de 3 points cliqués sur l’image affichée, par défaut, on a 30 s pour cliquer sur les points, sinon ginput se désactive, l’option timeout = -1 permet de désactiver ce comportement
Sélection de 3 points sur le périmètre du domaine circulaire

Après cette étape, si on affiche coord3points, c’est une liste contenant les coordonnées des points dans leur ordre de clic : coord3points = [(x_1,y_1), (x_2,y_2), (x_3,y_3)] . On peut donc aller récupérer les points avec les indices de la liste.

[x1,y1] = coord3points[0]
[x2,y2] = coord3points[1]
[x3,y3] = coord3points[2]
Centre et rayon du domaine circulaire

Pour retrouver le centre et le rayon du domaine à partir de ces 3 points, il faut faire un peu d’algèbre. Je t’épargne les détails de la démonstration (dis-moi si tu veux que je fasse une petite note là-dessus). Pour faire simple, si on appelle (x_0,y_0) les coordonnées du centre du domaine et r son rayon, les équations suivantes sont vérifiées :

A.\begin{bmatrix} x_0 \\ y_0 \end{bmatrix} = B avec A = \begin{bmatrix} x_1 - x_2 & y_1 - y_2 \\ x_1 -x_3 & y_1 - y_3\end{bmatrix} et B = \begin{bmatrix} x_1^2 - x_2^2 + y_1^2 - y_2^2\\ x_1^2 - x_3^2 + y_1^2 - y_3^2\end{bmatrix}

r = \sqrt{(x_1 - x_0)^2 + (y_1 - y_0)^2 }

Tout ça donne (xo,yo) = A^{-1}.B. Sur Python, la libraire numpy fournit les fonctions linalg.inv() pour calculer l’inverser d’une matrice et dot() pour effectuer le produit matriciel. En résumé, niveau code, l’étape de détermination des coordonnées du centre et du calcul du rayon peut s’écrire comme suit :

# Construction des matrices A et B à partir des coordonnées des 3 points cliqués
A = -2* np.array([[x1-x2,y1-y2 ],
                  [x1-x3,y1-y3]]);
B = -np.array([[x1**2 - x2**2 + y1**2 - y2**2], 
                [x1**2 - x3**2 + y1**2 - y3**2]])

# Calcul des coordonnées du centre dans le repère x,y
[x0,y0] = np.dot(np.linalg.inv(A),B)

# Equivalents des coordonnées en ligne,colonne
i0 = y0;
j0 = x0;

# Calcul du rayon du domaine circulaire
r = np.sqrt((y1-y0)**2 + (x1-x0)**2); 
# On aurait pu aussi faire r = np.sqrt((y2-y0)**2 + (x2-x0)**2) ou r = np.sqrt((y3-y0)**2 + (x3-x0)**2)

Et voilà, le travail est fait 🙂 ! Maintenant qu’on connait le centre et le rayon du domaine circulaire, on peut vérifier que tout ça marche bien en appliquant un masque blanc sur la région non-utile par exemple. Mais comme cet article est déjà suffisamment long comme ça, si tu veux en savoir plus sur la création de ce masque c’est par ici dans un autre article 😉.

Partager

Laisser un commentaire

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