web-dev-qa-db-fra.com

écriture d'une détection de cercle robuste (couleur et taille invariante) avec opencv (basé sur la transformation de Hough ou d'autres fonctionnalités)

J'ai écrit le code python très simple pour trouver des cercles dans une image:

import cv
import numpy as np

WAITKEY_DELAY_MS = 10
STOP_KEY = 'q'

cv.NamedWindow("image - press 'q' to quit", cv.CV_WINDOW_AUTOSIZE);
cv.NamedWindow("post-process", cv.CV_WINDOW_AUTOSIZE);

key_pressed = False
while key_pressed != STOP_KEY:

    # grab image
    orig = cv.LoadImage('circles3.jpg')

    # create tmp images
    grey_scale = cv.CreateImage(cv.GetSize(orig), 8, 1)
    processed = cv.CreateImage(cv.GetSize(orig), 8, 1)


    cv.Smooth(orig, orig, cv.CV_GAUSSIAN, 3, 3)

    cv.CvtColor(orig, grey_scale, cv.CV_RGB2GRAY)

    # do some processing on the grey scale image
    cv.Erode(grey_scale, processed, None, 10)
    cv.Dilate(processed, processed, None, 10)
    cv.Canny(processed, processed, 5, 70, 3)
    cv.Smooth(processed, processed, cv.CV_GAUSSIAN, 15, 15)

    storage = cv.CreateMat(orig.width, 1, cv.CV_32FC3)

    # these parameters need to be adjusted for every single image
    HIGH = 50
    LOW = 140

    try: 
        # extract circles
        cv.HoughCircles(processed, storage, cv.CV_HOUGH_GRADIENT, 2, 32.0, HIGH, LOW)

        for i in range(0, len(np.asarray(storage))):
            print "circle #%d" %i
            Radius = int(np.asarray(storage)[i][0][2])
            x = int(np.asarray(storage)[i][0][0])
            y = int(np.asarray(storage)[i][0][1])
            center = (x, y)

            # green dot on center and red circle around
            cv.Circle(orig, center, 1, cv.CV_RGB(0, 255, 0), -1, 8, 0)
            cv.Circle(orig, center, Radius, cv.CV_RGB(255, 0, 0), 3, 8, 0)

            cv.Circle(processed, center, 1, cv.CV_RGB(0, 255, 0), -1, 8, 0)
            cv.Circle(processed, center, Radius, cv.CV_RGB(255, 0, 0), 3, 8, 0)

    except:
        print "nothing found"
        pass

    # show images
    cv.ShowImage("image - press 'q' to quit", orig)
    cv.ShowImage("post-process", processed)

    cv_key = cv.WaitKey(WAITKEY_DELAY_MS)
    key_pressed = chr(cv_key & 255)

Comme vous pouvez le voir dans les deux exemples suivants, la "qualité de recherche de cercle" varie beaucoup:

CASE1:

input1detection1post-processed1

CASE2:

input2detection2post-processed2

Case1 et Case2 sont fondamentalement la même image, mais l'algorithme détecte toujours des cercles différents. Si je présente à l'algorithme une image avec des cercles de tailles différentes, la détection des cercles pourrait même échouer complètement. Cela est principalement dû aux paramètres HIGH et LOW qui doivent être ajustés individuellement pour chaque nouvelle image.

Donc ma question: quelles sont les différentes possibilités pour rendre cet algorithme plus robuste? La taille et la couleur doivent être invariables afin que différents cercles de couleurs différentes et de tailles différentes soient détectés. Peut-être que l'utilisation de la transformation de Hough n'est pas la meilleure façon de faire les choses? Existe-t-il de meilleures approches?

47
memyself

Ce qui suit est basé sur mon expérience en tant que chercheur en vision. De votre question, vous semblez être intéressé par les algorithmes et méthodes possibles plutôt que par un morceau de code fonctionnel. Je donne d'abord un script rapide et sale Python pour vos exemples d'images et certains résultats sont montrés pour prouver qu'il pourrait éventuellement résoudre votre problème. Après les avoir éliminés, j'essaie de répondre à vos questions concernant les algorithmes de détection robustes.

Résultats rapides

Quelques exemples d'images (toutes les images à part la vôtre sont téléchargées depuis flickr.com et sont sous licence CC) avec les cercles détectés (sans changer/régler aucun paramètre, exactement le code suivant est utilisé pour extraire les cercles dans toutes les images): detected blobs in the sample image 1detected blobs in the sample image 2lots of circlesblobs in the flickr image 1

Code (basé sur le détecteur Blob MSER)

Et voici le code:

import cv2
import math
import numpy as np

d_red = cv2.cv.RGB(150, 55, 65)
l_red = cv2.cv.RGB(250, 200, 200)

orig = cv2.imread("c.jpg")
img = orig.copy()
img2 = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

detector = cv2.FeatureDetector_create('MSER')
fs = detector.detect(img2)
fs.sort(key = lambda x: -x.size)

def supress(x):
        for f in fs:
                distx = f.pt[0] - x.pt[0]
                disty = f.pt[1] - x.pt[1]
                dist = math.sqrt(distx*distx + disty*disty)
                if (f.size > x.size) and (dist<f.size/2):
                        return True

sfs = [x for x in fs if not supress(x)]

for f in sfs:
        cv2.circle(img, (int(f.pt[0]), int(f.pt[1])), int(f.size/2), d_red, 2, cv2.CV_AA)
        cv2.circle(img, (int(f.pt[0]), int(f.pt[1])), int(f.size/2), l_red, 1, cv2.CV_AA)

h, w = orig.shape[:2]
vis = np.zeros((h, w*2+5), np.uint8)
vis = cv2.cvtColor(vis, cv2.COLOR_GRAY2BGR)
vis[:h, :w] = orig
vis[:h, w+5:w*2+5] = img

cv2.imshow("image", vis)
cv2.imwrite("c_o.jpg", vis)
cv2.waitKey()
cv2.destroyAllWindows()

Comme vous pouvez le voir, il est basé sur le détecteur de blob MSER . Le code ne pré-traite pas l'image en dehors du simple mappage en niveaux de gris. On s'attend donc à manquer ces taches jaunes pâles dans vos images.

Théorie

En bref: vous ne nous dites pas ce que vous savez sur le problème à part donner seulement deux exemples d'images sans les décrire. Ici, j'explique pourquoi, à mon humble avis, il est important d'avoir plus d'informations sur le problème avant de demander quelles sont les méthodes efficaces pour attaquer le problème.

Retour à la question principale: quelle est la meilleure méthode pour résoudre ce problème? Voyons cela comme un problème de recherche. Pour simplifier la discussion, supposons que nous recherchons des cercles avec une taille/un rayon donnés. Ainsi, le problème se résume à trouver les centres. Chaque pixel est un centre candidat, par conséquent, l'espace de recherche contient tous les pixels.

P = {p1, ..., pn} 
P: search space
p1...pn: pixels

Pour résoudre ce problème de recherche, deux autres fonctions doivent être définies:

E(P) : enumerates the search space
V(p) : checks whether the item/pixel has the desirable properties, the items passing the check are added to the output list

En supposant que la complexité de l'algorithme n'a pas d'importance, la recherche exhaustive ou par force brute peut être utilisée dans laquelle E prend chaque pixel et passe à V. Dans les applications en temps réel, il est important de réduire l'espace de recherche et d'optimiser l'efficacité de calcul de V .

Nous nous rapprochons de la question principale. Comment définir V, pour être plus précis quelles propriétés des candidats devraient être des mesures et comment faire résoudre le problème de dichotomie de les diviser en souhaitables et indésirables. L'approche la plus courante consiste à trouver des propriétés qui peuvent être utilisées pour définir des règles de décision simples basées sur la mesure des propriétés. C'est ce que vous faites par essais et erreurs. Vous programmez un classificateur en apprenant à partir d'exemples positifs et négatifs. C'est parce que les méthodes que vous utilisez n'ont aucune idée de ce que vous voulez faire. Vous devez ajuster/régler les paramètres de la règle de décision et/ou prétraiter les données de manière à réduire la variation des propriétés (des candidats souhaitables) utilisées par la méthode pour le problème de dichotomie. Vous pouvez utiliser un algorithme d'apprentissage automatique pour trouver les valeurs de paramètres optimales pour un ensemble d'exemples donné. Il existe une multitude d'algorithmes d'apprentissage, des arbres de décision à la programmation génétique, que vous pouvez utiliser pour ce problème. Vous pouvez également utiliser un algorithme d'apprentissage pour trouver les valeurs de paramètres optimales pour plusieurs algorithmes de détection de cercle et voir lequel donne une meilleure précision. Cela alourdit l'algorithme d'apprentissage dont vous avez juste besoin pour collecter des exemples d'images.

L'autre approche pour améliorer la robustesse qui est souvent négligée consiste à utiliser des informations supplémentaires facilement disponibles. Si vous connaissez la couleur des cercles avec un effort supplémentaire pratiquement nul, vous pouvez améliorer considérablement la précision du détecteur. Si vous connaissiez la position des cercles sur le plan et que vous vouliez détecter les cercles imagés, vous devez vous rappeler que la transformation entre ces deux ensembles de positions est décrite par une homographie 2D. Et l'homographie peut être estimée en utilisant seulement quatre points. Ensuite, vous pourriez améliorer la robustesse pour avoir une méthode solide comme le roc. La valeur des connaissances spécifiques à un domaine est souvent sous-estimée. Regardez-le de cette façon, dans la première approche, nous essayons d'approximer certaines règles de décision en fonction d'un nombre limité d'échantillons. Dans la seconde approche, nous connaissons les règles de décision et n'avons besoin que de trouver un moyen de les utiliser efficacement dans un algorithme.

Sommaire

Pour résumer, il existe deux approches pour améliorer la précision/robustesse de la solution:

  1. Basé sur les outils : trouver un algorithme plus facile à utiliser/avec moins de paramètres/peaufiner l'algorithme/automatiser ce processus en utilisant des algorithmes d'apprentissage automatique
  2. Basé sur l'information : utilisez-vous toutes les informations facilement disponibles? Dans la question, vous ne mentionnez pas ce que vous savez du problème.

Pour ces deux images que vous avez partagées, j'utiliserais un détecteur de taches et non la méthode HT. Pour la soustraction de l'arrière-plan, je suggère d'essayer d'estimer la couleur de l'arrière-plan car dans les deux images, elle ne varie pas tandis que la couleur des cercles varie. Et la majeure partie de la zone est nue.

36
fireant

Il s'agit d'un grand problème de modélisation. J'ai les recommandations/idées suivantes:

  1. Divisez l'image en RVB puis traitez.
  2. prétraitement.
  3. Recherche dynamique de paramètres.
  4. Ajoutez des contraintes.
  5. Soyez sûr de ce que vous essayez de détecter.

Plus en détail:

1: Comme indiqué dans d'autres réponses, la conversion directe en niveaux de gris supprime trop d'informations - tous les cercles avec une luminosité similaire à l'arrière-plan seront perdus. Il est préférable de considérer les canaux de couleur de manière isolée ou dans un espace colorimétrique différent. Il y a à peu près deux façons d'aller ici: effectuer HoughCircles sur chaque canal prétraité de manière isolée, puis combiner les résultats, ou, traiter les canaux, puis les combiner, puis utiliser HoughCircles. Dans ma tentative ci-dessous, j'ai essayé la deuxième méthode, la division en canaux RVB, le traitement, puis la combinaison. Méfiez-vous de trop saturer l'image lors de la combinaison, j'utilise cv.And pour éviter ce problème (à ce stade, mes cercles sont toujours des anneaux/disques noirs sur fond blanc).

2: Le pré-traitement est assez délicat et il est souvent préférable de jouer avec. J'ai utilisé AdaptiveThreshold qui est une méthode de convolution vraiment puissante qui peut améliorer les bords d'une image en seuillant les pixels en fonction de leur moyenne locale (des processus similaires se produisent également dans la première voie du système visuel des mammifères). Ceci est également utile car il réduit le bruit. J'ai utilisé dilate/erode en un seul passage. Et j'ai gardé les autres paramètres comme vous les aviez. Il semble que l'utilisation de Canny avant HoughCircles aide beaucoup à trouver des "cercles remplis", il est donc préférable de le conserver. Ce prétraitement est assez lourd et peut conduire à des faux positifs avec un peu plus de "cercles blobby", mais dans notre cas, cela est peut-être souhaitable?

3: Comme vous l'avez noté, le paramètre HoughCircles param2 (votre paramètre LOW) doit être ajusté pour chaque image afin d'obtenir une solution optimale, en fait à partir de docs :

Plus il est petit, plus il peut y avoir de faux cercles.

Le problème est que le sweet spot va être différent pour chaque image. Je pense que la meilleure approche ici consiste à définir une condition et à effectuer une recherche parmi différents param2 valeurs jusqu'à ce que cette condition soit remplie. Vos images montrent des cercles qui ne se chevauchent pas et lorsque param2 est trop faible, nous obtenons généralement des charges de cercles qui se chevauchent. Je suggère donc de rechercher:

nombre maximum de cercles non superposés et non contenus

Nous continuons donc d'appeler HoughCircles avec différentes valeurs de param2 jusqu'à ce que cela soit atteint. Je le fais dans mon exemple ci-dessous, juste en incrémentant param2 jusqu'à ce qu'il atteigne l'hypothèse de seuil. Ce serait beaucoup plus rapide (et assez facile à faire) si vous effectuez une recherche binaire pour trouver quand cela est rencontré, mais vous devez être prudent avec la gestion des exceptions car opencv renvoie souvent des erreurs pour les valeurs d'apparence innocente de param2 (au moins sur mon installation). Une autre condition à laquelle nous serions très utiles pour faire face serait le nombre de cercles.

4: Y a-t-il d'autres contraintes que nous pouvons ajouter au modèle? Le plus de choses que nous pouvons dire à notre modèle, la tâche facile que nous pouvons faire pour détecter les cercles. Par exemple, savons-nous:

  • Le nombre de cercles. - même une limite supérieure ou inférieure est utile.
  • Couleurs possibles des cercles, ou du fond, ou des "non-cercles".
  • Leurs tailles.
  • Où ils peuvent être dans une image.

5: Certains des blobs de vos images ne peuvent être appelés que des cercles! Considérez les deux `` taches non circulaires '' dans votre deuxième image, mon code ne peut pas les trouver (bien!), Mais ... si je les "photoshope" pour qu'elles soient plus circulaires, mon code peut les trouver ... Peut-être que si vous voulez détecter des choses qui ne sont pas des cercles, une approche différente telle que Tim Lukins peut-être mieux.

Problèmes

En effectuant un prétraitement intensif AdaptiveThresholding et `Canny ', il peut y avoir beaucoup de distorsion des entités dans une image, ce qui peut entraîner une détection de faux cercle ou un rapport de rayon incorrect. Par exemple, un gros disque solide après traitement peut apparaître comme un anneau, donc HughesCircles peut trouver l'anneau intérieur. De plus, même les documents notent que:

... généralement la fonction détecte bien les centres des cercles, mais elle peut ne pas trouver les rayons corrects.

Si vous avez besoin d'une détection des rayons plus précise, je suggère l'approche suivante (non mise en œuvre):

  • Sur l'image d'origine, trace de rayon à partir du centre du cercle signalé, dans une croix en expansion (4 rayons: haut/bas/gauche/droite)
  • Faites-le séparément dans chaque canal RVB
  • Combinez ces informations pour chaque canal pour chaque rayon d'une manière sensée (par exemple, flip, offset, échelle, etc. si nécessaire)
  • prenez la moyenne des premiers pixels de chaque rayon, utilisez-la pour détecter où une déviation significative sur le rayon se produit.
  • Ces 4 points sont des estimations de points sur la circonférence.
  • Utilisez ces quatre estimations pour déterminer un rayon plus précis et une position centrale (!).
  • Cela pourrait être généralisé en utilisant un anneau en expansion au lieu de quatre rayons.

Résultats

Le code à la fin fait assez bien la plupart du temps, ces exemples ont été faits avec du code comme indiqué:

Détecte tous les cercles de votre première image:enter image description here

A quoi ressemble l'image prétraitée avant l'application du filtre dynamique (différents cercles de couleur sont très visibles):enter image description here

Détecte tout sauf deux (blobs) dans la deuxième image:enter image description here

Seconde image modifiée (les taches sont liées au cercle et un grand ovale plus circulaire, améliorant ainsi la détection), tous détectés: enter image description here

Fait assez bien pour détecter les centres dans cette peinture de Kandinsky (je ne trouve pas d'anneaux concentriques en raison de la condition aux limites). enter image description here

Code:

import cv
import numpy as np

output = cv.LoadImage('case1.jpg')
orig = cv.LoadImage('case1.jpg')

# create tmp images
rrr=cv.CreateImage((orig.width,orig.height), cv.IPL_DEPTH_8U, 1)
ggg=cv.CreateImage((orig.width,orig.height), cv.IPL_DEPTH_8U, 1)
bbb=cv.CreateImage((orig.width,orig.height), cv.IPL_DEPTH_8U, 1)
processed = cv.CreateImage((orig.width,orig.height), cv.IPL_DEPTH_8U, 1)
storage = cv.CreateMat(orig.width, 1, cv.CV_32FC3)

def channel_processing(channel):
    pass
    cv.AdaptiveThreshold(channel, channel, 255, adaptive_method=cv.CV_ADAPTIVE_THRESH_MEAN_C, thresholdType=cv.CV_THRESH_BINARY, blockSize=55, param1=7)
    #mop up the dirt
    cv.Dilate(channel, channel, None, 1)
    cv.Erode(channel, channel, None, 1)

def inter_centre_distance(x1,y1,x2,y2):
    return ((x1-x2)**2 + (y1-y2)**2)**0.5

def colliding_circles(circles):
    for index1, circle1 in enumerate(circles):
        for circle2 in circles[index1+1:]:
            x1, y1, Radius1 = circle1[0]
            x2, y2, Radius2 = circle2[0]
            #collision or containment:
            if inter_centre_distance(x1,y1,x2,y2) < Radius1 + Radius2:
                return True

def find_circles(processed, storage, LOW):
    try:
        cv.HoughCircles(processed, storage, cv.CV_HOUGH_GRADIENT, 2, 32.0, 30, LOW)#, 0, 100) great to add circle constraint sizes.
    except:
        LOW += 1
        print 'try'
        find_circles(processed, storage, LOW)
    circles = np.asarray(storage)
    print 'number of circles:', len(circles)
    if colliding_circles(circles):
        LOW += 1
        storage = find_circles(processed, storage, LOW)
    print 'c', LOW
    return storage

def draw_circles(storage, output):
    circles = np.asarray(storage)
    print len(circles), 'circles found'
    for circle in circles:
        Radius, x, y = int(circle[0][2]), int(circle[0][0]), int(circle[0][1])
        cv.Circle(output, (x, y), 1, cv.CV_RGB(0, 255, 0), -1, 8, 0)
        cv.Circle(output, (x, y), Radius, cv.CV_RGB(255, 0, 0), 3, 8, 0)

#split image into RGB components
cv.Split(orig,rrr,ggg,bbb,None)
#process each component
channel_processing(rrr)
channel_processing(ggg)
channel_processing(bbb)
#combine images using logical 'And' to avoid saturation
cv.And(rrr, ggg, rrr)
cv.And(rrr, bbb, processed)
cv.ShowImage('before canny', processed)
# cv.SaveImage('case3_processed.jpg',processed)
#use canny, as HoughCircles seems to prefer ring like circles to filled ones.
cv.Canny(processed, processed, 5, 70, 3)
#smooth to reduce noise a bit more
cv.Smooth(processed, processed, cv.CV_GAUSSIAN, 7, 7)
cv.ShowImage('processed', processed)
#find circles, with parameter search
storage = find_circles(processed, storage, 100)
draw_circles(storage, output)
# show images
cv.ShowImage("original with circles", output)
cv.SaveImage('case1.jpg',output)

cv.WaitKey(0)
28
fraxel

Ah, oui… les anciens invariants couleur/taille pour les cercles (AKA la transformée de Hough est trop spécifique et pas robuste) ...

Dans le passé, je me suis beaucoup appuyé sur les fonctions analyse structurelle et de forme d'OpenCV. Vous pouvez vous faire une très bonne idée de ce qui est possible à partir du dossier "samples" - en particulier fitellipse.py et squares.py.

Pour votre explication, je présente une version hybride de ces exemples et basée sur votre source d'origine. Les contours détectés sont en vert et les ellipses ajustées en rouge.

enter image description here

Ce n'est pas encore tout à fait là:

  • Les étapes de prétraitement nécessitent un peu de peaufinage pour détecter les cercles les plus faibles.
  • Vous pouvez tester davantage le contour pour déterminer s'il s'agit d'un cercle ou non ...

Bonne chance!

import cv
import numpy as np

# grab image
orig = cv.LoadImage('circles3.jpg')

# create tmp images
grey_scale = cv.CreateImage(cv.GetSize(orig), 8, 1)
processed = cv.CreateImage(cv.GetSize(orig), 8, 1)

cv.Smooth(orig, orig, cv.CV_GAUSSIAN, 3, 3)

cv.CvtColor(orig, grey_scale, cv.CV_RGB2GRAY)

# do some processing on the grey scale image
cv.Erode(grey_scale, processed, None, 10)
cv.Dilate(processed, processed, None, 10)
cv.Canny(processed, processed, 5, 70, 3)
cv.Smooth(processed, processed, cv.CV_GAUSSIAN, 15, 15)

#storage = cv.CreateMat(orig.width, 1, cv.CV_32FC3)
storage = cv.CreateMemStorage(0)

contours = cv.FindContours(processed, storage, cv.CV_RETR_EXTERNAL)
# N.B. 'processed' image is modified by this!

#contours = cv.ApproxPoly (contours, storage, cv.CV_POLY_APPROX_DP, 3, 1) 
# If you wanted to reduce the number of points...

cv.DrawContours (orig, contours, cv.RGB(0,255,0), cv.RGB(255,0,0), 2, 3, cv.CV_AA, (0, 0)) 

def contour_iterator(contour):
  while contour:
    yield contour
    contour = contour.h_next()

for c in contour_iterator(contours):
  # Number of points must be more than or equal to 6 for cv.FitEllipse2
  if len(c) >= 6:
    # Copy the contour into an array of (x,y)s
    PointArray2D32f = cv.CreateMat(1, len(c), cv.CV_32FC2)

    for (i, (x, y)) in enumerate(c):
      PointArray2D32f[0, i] = (x, y)

    # Fits ellipse to current contour.
    (center, size, angle) = cv.FitEllipse2(PointArray2D32f)

    # Convert ellipse data from float to integer representation.
    center = (cv.Round(center[0]), cv.Round(center[1]))
    size = (cv.Round(size[0] * 0.5), cv.Round(size[1] * 0.5))

    # Draw ellipse
    cv.Ellipse(orig, center, size, angle, 0, 360, cv.RGB(255,0,0), 2,cv.CV_AA, 0)

# show images
cv.ShowImage("image - press 'q' to quit", orig)
#cv.ShowImage("post-process", processed)
cv.WaitKey(-1)

MODIFIER:

Juste une mise à jour pour dire que je crois qu'un thème majeur de toutes ces réponses est qu'il existe une multitude d'hypothèses et de contraintes supplémentaires qui peuvent être appliquées à ce que vous cherchez à reconnaître comme circulaire. Ma propre réponse ne fait aucune prétention à cela - ni dans le prétraitement de bas niveau ni dans l'ajustement géométrique de haut niveau. Le fait que beaucoup de cercles ne sont pas vraiment si ronds en raison de la façon dont ils sont dessinés ou des transformations non affines/projectives de l'image, et avec les autres propriétés dans la façon dont ils sont rendus/capturés (couleur, bruit, éclairage, Épaisseur du bord) - tous les résultats dans un nombre illimité de cercles candidats possibles dans une seule image.

Il existe des techniques beaucoup plus sophistiquées. Mais ils vous coûteront. Personnellement, j'aime l'idée @fraxel d'utiliser le seuil addaptif. C'est rapide, fiable et raisonnablement robuste. Vous pouvez ensuite tester davantage les contours finaux (par exemple, utiliser les moments Hu) ou les raccords avec un simple test de rapport de l'axe de l'ellipse - par exemple si ((min (taille)/max (taille))> 0,7).

Comme toujours avec la vision par ordinateur, il existe une tension entre le pragmatisme, les principes et la parcimonie. Comme j'aime dire aux gens qui pensent que le CV est facile, ce n'est pas - c'est en fait un problème AI complete . Le mieux que vous puissiez souvent espérer en dehors de cela est quelque chose qui fonctionne la plupart du temps.

12
tiluki

En parcourant votre code, j'ai remarqué ce qui suit:

  • Conversion en niveaux de gris. Je comprends pourquoi vous le faites, mais réalisez que vous y jetez des informations. Comme vous le voyez dans les images "post-process", vos cercles jaunes ont la même intensité que l'arrière-plan, juste dans une couleur différente.

  • Détection des bords après élimination du bruit (eré/dilaté). Cela ne devrait pas être nécessaire; Canny devrait s'en occuper.

  • Détection Canny Edge. Vos cercles "ouverts" ont deux bords, un bord intérieur et extérieur. Puisqu'ils sont assez proches, le filtre de Canny gauss peut les additionner. Si ce n'est pas le cas, vous aurez deux bords rapprochés. C'est à dire. avant Canny, vous avez des cercles ouverts et remplis. Ensuite, vous avez respectivement 0/2 et 1 Edge. Étant donné que Hough appelle à nouveau Canny, dans le premier cas, les deux bords peuvent être lissés ensemble (en fonction de la largeur initiale), c'est pourquoi l'algorithme principal de Hough peut traiter les cercles ouverts et remplis de la même manière.

Donc, ma première recommandation serait de changer la cartographie en niveaux de gris. N'utilisez pas l'intensité, mais utilisez la teinte/saturation/valeur. En outre, utilisez une approche différentielle - vous recherchez des bords. Donc, calculez une transformation HSV, lissez une copie, puis faites la différence entre la copie d'origine et la copie lissée. Cela vous donnera dH, dS, dV valeurs (variation locale de teinte, saturation, valeur) pour chaque point. Équerrez et ajoutez pour obtenir une image unidimensionnelle, avec des pics près de tous les bords (intérieur et extérieur).

Ma deuxième recommandation serait la normalisation locale, mais je ne sais pas si c'est même nécessaire. L'idée est que vous ne vous souciez pas particulièrement de la valeur exacte du signal Edge que vous avez sorti, il devrait de toute façon être binaire (Edge ou non). Par conséquent, vous pouvez normaliser chaque valeur en la divisant par une moyenne locale (où local est dans l'ordre de grandeur de votre taille Edge).

9
MSalters

La transformation de Hough utilise un "modèle" pour trouver certaines fonctionnalités dans une image (généralement) détectée par Edge, comme vous le savez peut-être. Dans le cas de HoughCircles ce modèle est un cercle parfait. Cela signifie qu'il n'existe probablement pas de combinaison de paramètres permettant de détecter les cercles plus irréguliers et en forme d'ellipse de votre image sans augmenter le nombre de faux positifs. D'un autre côté, en raison du mécanisme de vote sous-jacent, un cercle parfait non fermé ou un cercle parfait avec une "bosselure" pourrait apparaître régulièrement. Donc selon votre sortie attendue vous pouvez ou non vouloir utiliser cette méthode.

Cela dit, il y a quelques choses que je vois qui pourraient vous aider sur votre chemin avec cette fonction:

  1. HoughCircles appelle Canny en interne, donc je suppose que vous pouvez laisser cet appel.
  2. param1 (Que vous appelez HIGH) est généralement initialisé autour d'une valeur de 200. Il est utilisé comme paramètre de l'appel interne à Canny: cv.Canny(processed, cannied, HIGH, HIGH/2). Il peut être utile d'exécuter Canny vous-même comme ceci pour voir comment le paramètre HIGH affecte l'image utilisée par la transformation Hough.
  3. param2 (Que vous appelez LOW) est généralement initialisé autour d'une valeur 100. C'est le seuil de vote pour les accumulateurs de la transformée de Hough. Une valeur plus élevée signifie plus de faux négatifs, moins de faux positifs. Je crois que c'est le premier avec lequel vous voulez commencer à jouer.

Réf: http://docs.opencv.org/3.0-beta/modules/imgproc/doc/feature_detection.html#houghcircles

Mise à jour concernant les cercles remplis : Une fois que vous avez trouvé les formes de cercle avec la transformation de Hough, vous pouvez tester si elles sont remplies en échantillonnant la couleur du contour et en comparant à un ou plusieurs points à l'intérieur du cercle supposé. Vous pouvez également comparer un ou plusieurs points à l'intérieur du cercle supposé à une couleur d'arrière-plan donnée. Le cercle est rempli si la comparaison précédente réussit, ou dans le cas de la comparaison alternative s'il échoue.

6
Eric

Ok en regardant les images. Je suggère d'utiliser **Active Contours**

  • Contours actifs La bonne chose à propos des contours actifs est qu'ils s'adaptent presque parfaitement à la forme donnée. Que ce soit des carrés ou des triangles et dans votre cas, ils sont les candidats parfaits.
  • Si vous pouvez extraire le centre des cercles, c'est parfait. Les contours actifs ont toujours besoin d'un point de départ à partir duquel ils peuvent grandir ou se rétrécir pour s'adapter. Il n'est pas nécessaire que les centres soient toujours alignés sur le centre. Un petit décalage sera toujours correct.
  • Et dans votre cas, si vous laissez les contours croître du centre vers l'extérieur, ils doivent reposer sur les limites du cercle.
  • Notez que les contours actifs qui grandissent ou rétrécissent utilisent énergie du ballon ce qui signifie que vous pouvez définir la direction des contours, vers l'intérieur ou vers l'extérieur.
  • Vous auriez probablement besoin d'utiliser l'image dégradée en échelle de gris. Mais vous pouvez toujours essayer la couleur également. Si ça marche!
  • Et si vous ne fournissez pas de centres, ajoutez de nombreux contours actifs, faites ensuite grandir/rétrécir. Les contours qui se déposent sont conservés, ceux qui ne sont pas réglés sont jetés. Il s'agit d'une approche par force brute. Sera intensif en CPU. Mais il faudra un travail plus minutieux pour vous assurer de laisser les contours corrects et de jeter les mauvais.

J'espère que de cette façon, vous pourrez résoudre le problème.

2
user349026