5.27. Détection de cercles et de rectangles¶
Les lignes et segments couvrent les contours droits de la trame capturée, mais bon nombre des caractéristiques que la caméra recherche dans le monde réel ne sont pas droites. Une pièce de monnaie posée sur un bureau est un cercle. Une étiquette imprimée, un pense-bête, le dessus d’une boîte vue de biais constituent un quadrilatère. Le module image expose un détecteur dédié à chacun : une recherche de type Hough pour les cercles et une recherche dérivée d’AprilTag pour les formes à quatre côtés.
Les deux méthodes suivent le même modèle que les détecteurs de lignes : threshold contrôle le nombre de votes nécessaires à une détection, roi restreint la recherche, et les objets renvoyés portent à la fois une position et une amplitude de confiance. Mais les espaces de paramètres et les bonnes valeurs par défaut diffèrent suffisamment pour mériter une présentation explicite.
5.27.1. Cercles de Hough¶
find_circles() exécute la variante circulaire de la transformée de Hough. Chaque pixel de contour issu du prétraitement de Sobel vote pour tous les cercles susceptibles de passer par lui ; les cercles qui recueillent suffisamment de votes sont renvoyés.
circles = img.find_circles(threshold=3500,
x_margin=10, y_margin=10, r_margin=10,
r_min=10, r_max=80, r_step=2)
for c in circles:
img.draw_circle((c.x, c.y, c.r), color=(255, 0, 0))
threshold est la somme minimale des amplitudes de contour de Sobel le long du cercle candidat. Les cercles plus grands tracent plus de pixels et nécessitent donc des seuils plus élevés pour être validés ; une valeur qui détecte une pièce d’un rayon de 20 pixels se déclenchera aussi sur le bruit autour d’un contour de 100 pixels, tandis qu’une valeur réglée pour la grande pièce manquera la petite. Lorsque la plage de rayons cible est connue, le bon seuil évolue avec la circonférence : approximativement threshold = 50 * 2 * pi * r constitue un bon point de départ, la valeur exacte découlant d’une brève phase de réglage.
r_min, r_max et r_step définissent la recherche de rayon. Sans bornes, le détecteur explorerait chaque rayon, de quelques pixels jusqu’à la demi-largeur de l’image, ce qui est à la fois lent et propice aux faux positifs. Régler r_min et r_max pour encadrer la taille cible attendue avec une marge généreuse (par exemple r_min=15, r_max=25 pour une pièce connue pour mesurer environ 20 pixels) réduit considérablement le travail et améliore le rapport signal/bruit des votes. r_step contrôle la granularité de la recherche ; des pas plus grands s’exécutent plus vite et risquent de manquer un cercle dont le rayon réel se situe entre deux valeurs échantillonnées. La valeur par défaut r_step=2 est un compromis raisonnable.
x_margin, y_margin et r_margin contrôlent la fusion des détections voisines, de la même manière que theta_margin et rho_margin pour la détection de lignes. Un seul cercle physique dans l’image vote pour un groupe de cercles candidats dont les centres et les rayons concordent à quelques pixels près ; les marges réduisent chaque groupe à son pic avant la construction de la liste de résultats. Des marges plus grandes renvoient moins de détections, plus fiables ; des marges plus petites renvoient davantage de détections avec d’éventuels quasi-doublons.
x_stride et y_stride font progresser le balayage de vote, de la même manière que dans les autres détecteurs. Les valeurs par défaut de 2 et 1 conviennent à la plupart des images ; les augmenter toutes deux à 4 constitue le compromis de vitesse standard pour une image connue pour contenir de grands cercles.
Chaque Circle renvoyé porte x, y, r (le centre et le rayon) ainsi que magnitude (le total des votes, utile comme score de confiance pour le tri ou le filtrage). Redessiner la détection dans la trame se fait en un seul appel : draw_circle() prend le triplet (x, y, r), disponible directement sous la forme (c.x, c.y, c.r) à partir du résultat.
5.27.2. Rectangles¶
find_rects() emprunte le détecteur de quadrilatères au pipeline AprilTag : la même routine qui localise le carré noir autour d’un tag est exposée à part comme un détecteur de rectangles à usage général.
rects = img.find_rects(threshold=12000)
for r in rects:
img.draw_rectangle(r.rect, color=(0, 255, 0))
for corner in r.corners:
img.draw_circle((corner[0], corner[1], 4),
color=(0, 255, 0))
threshold est la somme minimale des amplitudes de contour autour du périmètre du rectangle. Un rectangle imprimé en noir sur blanc dans une trame bien éclairée dépasse facilement 10000 ; un rectangle peu marqué sur un arrière-plan texturé peut devoir descendre à 2000, au prix d’un compromis entre faux positifs et sensibilité. Comme pour le détecteur de cercles, la bonne valeur découle d’une rapide phase de réglage avec les cibles voulues dans le champ.
Le détecteur est projectif : il trouve des quadrilatères dont les côtés sont droits mais pas nécessairement parallèles ou alignés sur les axes. Une étiquette vue de biais ressemble à un trapèze dans l’image, et le détecteur de rectangles la trouve correctement ; un rectangle aligné sur les axes n’est que le cas dégénéré où les quatre coins forment justement une boîte à angles droits. roi restreint la recherche ; les autres arguments nommés prennent leurs valeurs par défaut du pipeline AprilTag et nécessitent rarement un réglage.
Chaque Rect renvoyé porte la boîte englobante alignée sur les axes (x, y, w, h, ainsi que le quadruplet rect qu’attend draw_rectangle()) et les quatre coins détectés sous la forme corners. La boîte englobante est ce que l’application utilise pour une position et une taille approximatives ; les coins décrivent le quadrilatère projectif lui-même. Lorsque la caméra observe une cible plane sous un angle et que l’application doit corriger l’effet de trapèze (lire le texte d’une étiquette, échantillonner la couleur d’une zone plane), les coins alimentent directement rotation_corr() via le mot-clé corners= (voir correction d’objectif et de perspective), et la sortie est le rectangle redressé prêt pour l’analyse suivante.
Avertissement
Comme le détecteur est réglé pour ce dont le pipeline AprilTag a besoin (des quadrilatères aux bordures nettes et fortement contrastées, comme le contour noir d’un tag sur du papier blanc), il ne s’agit pas d’une passe qui détecte tous les rectangles. Les rectangles à faible contraste, aux contours texturés ou entourés d’éléments chargés peuvent ne pas être détectés du tout. Son efficacité dépend de la situation : testez-le tôt sur les cibles réelles, avant de construire un pipeline autour de lui.
5.27.3. Quand le détecteur se trompe¶
Les cercles bénéficient tout particulièrement d’un préfiltrage de l’entrée. Une trame bruitée produit de nombreux pixels de contour parasites qui votent tous, et l’espace de Hough résultant présente de larges pics flous que le fusionneur peine à séparer. Une passe gaussian() ou mean() avant find_circles() lisse le bruit tout en laissant intacts les véritables contours ; le détecteur renvoie des pics plus nets en moins de temps.
Pour les rectangles, le mode de défaillance courant est l’inverse : un faible contraste entre le rectangle et son arrière-plan signifie que la somme des amplitudes de contour ne dépasse jamais threshold. Une passe histeq() qui redistribue la plage de luminosité sur toute l’étendue de 0 à 255 rétablit le contraste dont le détecteur a besoin. (Le contraste doit exister quelque part dans l’image ; l’égalisation d’histogramme ne peut qu’amplifier ce qui est déjà présent.)