Au détour de quelques questions que je me posais que la détection de forme, j'ai découvert les transformations de Hough. Ces transformations permettent, en jouant avec la géométrie de détecter par exemple des droites ou des cercles dans des images. Le principe est basé sur un accumulateur construit dans un espace réciproque à l'image dont les maxima marquent les formes probablement existantes dans l'image binarisée.

En quelques mots, pour des droites, l'idée est de balayer toutes les pentes et toutes ordonnées à l'origine et de compter le nombre de pixels matchant la droite de test avec l'image.

Scikit-image est une bibliothèque sous licence libre pour Python spécialisée dans la traitement d'image trouvant son socle technologique dans numpy et scipy. La transformation de Hough pour les lignes existait déjà, mais pas pour les cercles.

J'ai donc implémenté cette détection dont l'image ci-dessous illustre le résultat sur une photographie d'une série de pièces de monnaie. Dans l'exemple, on fait une tentative en précisant 2 rayons et en récupérant deux possibilités par rayon. Un des intérêts que je vois dans cette méthode est l'estimation de la position du centre de cercle qui peuvent être importants pour d'autres traitements postérieurs.

hough_transform


import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches

from skimage import data, filter
from skimage.transform import hough_circle
from skimage.feature import peak_local_max

# Load picture and detect edges
image = data.coins()[0:95, 70:370]
edges = filter.canny(filter.sobel(image), sigma=2.8)

fig, ax = plt.subplots(ncols=1, nrows=1, figsize=(6, 6))
ax.imshow(image, cmap=plt.cm.gray)

# Detect two radii radii = np.array([21, 25])
hough_res = hough_circle(edges, radii)

for radius, h in zip(radii, hough_res):
    # For each radius, keep two circles
    maxima = peak_local_max(h, num_peaks=2)
    for maximum in maxima:
        center_x, center_y = maximum - radii.max()
        circ = mpatches.Circle((center_y, center_x), radius, fill=False, edgecolor='red', linewidth=2)
        ax.add_patch(circ)

plt.show()

Petite remarque. J'ai appris aussi à cet occasion l'algorithme permettant de dessiner des cercles ou des lignes (Bresenham) et que celui-ci présentait le défaut ne pouvoir faire des cercles concentriques formant un disque plein. L'algorithme d'Andres est alors la solution pour ça (il supporte mieux la rotation aussi). Le premier était implémenté dans Scikit, pas le second, ce que j'ai corrigé. A noté que j'utilise Bresenham pour Hough car il me semble plus approprié (moins de redondances locales)