Détection des pièces (et ajustement des ellipses) sur une image

Je travaille actuellement sur un projet où j’essaie de détecter quelques pièces sur une surface plane (un bureau). Les pièces ne se chevauchent pas et ne sont pas cachées par d’autres objects. Mais il peut y avoir d’autres objects visibles et les conditions d’éclairage peuvent ne pas être parfaites.

Ainsi, chaque sharepointvrait être visible sous la forme d’une ellipse. Comme je ne connais pas la position de la caméra, la forme des ellipses peut varier, d’un cercle (vue du dessus) aux ellipses plates, en fonction de l’angle d’où les pièces sont filmées.

Mon problème est que je ne suis pas sûr de savoir comment extraire les pièces et enfin ajuster les ellipses par-dessus (ce que je cherche à faire pour faire d’autres calculs).

Pour l’instant, je viens de faire une première tentative en définissant une valeur de seuil dans OpenCV, en utilisant findContours () pour obtenir les lignes de contour et ajuster une ellipse. Malheureusement, les lignes de contour ne me donnent que rarement la forme des pièces (reflets, mauvais éclairage, …) et cette manière n’est pas non plus préférée car je ne veux pas que l’utilisateur définisse un seuil.

Une autre idée était d’utiliser une méthode de correspondance de modèle d’une ellipse sur cette image, mais comme je ne connais pas l’angle de la caméra ni la taille des ellipses, je ne pense pas que cela fonctionnerait bien …

Alors je voulais demander si quelqu’un pouvait me dire une méthode qui fonctionnerait dans mon cas …

Existe-t-il un moyen rapide d’extraire les trois pièces de l’image? Les calculs doivent être effectués en temps réel sur les appareils mobiles et la méthode ne doit pas être trop sensible pour des lumières différentes ou changeantes ou la couleur de l’arrière-plan.

Serait bien si quelqu’un pouvait me donner des conseils sur la méthode qui pourrait fonctionner pour moi …

Voici une source C99 implémentant l’approche traditionnelle (basée sur le doco OpenCV):

#include "cv.h" #include "highgui.h" #include  #ifndef M_PI #define M_PI 3.14159265358979323846 #endif // // We need this to be high enough to get rid of things that are too small too // have a definite shape. Otherwise, they will end up as ellipse false positives. // #define MIN_AREA 100.00 // // One way to tell if an object is an ellipse is to look at the relationship // of its area to its dimensions. If its actual occupied area can be estimated // using the well-known area formula Area = PI*A*B, then it has a good chance of // being an ellipse. // // This value is the maximum permissible error between actual and estimated area. // #define MAX_TOL 100.00 int main( int argc, char** argv ) { IplImage* src; // the first command line parameter must be file name of binary (black-n-white) image if( argc == 2 && (src=cvLoadImage(argv[1], 0))!= 0) { IplImage* dst = cvCreateImage( cvGetSize(src), 8, 3 ); CvMemStorage* storage = cvCreateMemStorage(0); CvSeq* contour = 0; cvThreshold( src, src, 1, 255, CV_THRESH_BINARY ); // // Invert the image such that white is foreground, black is background. // Dilate to get rid of noise. // cvXorS(src, cvScalar(255, 0, 0, 0), src, NULL); cvDilate(src, src, NULL, 2); cvFindContours( src, storage, &contour, sizeof(CvContour), CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE, cvPoint(0,0)); cvZero( dst ); for( ; contour != 0; contour = contour->h_next ) { double actual_area = fabs(cvContourArea(contour, CV_WHOLE_SEQ, 0)); if (actual_area < MIN_AREA) continue; // // FIXME: // Assuming the axes of the ellipse are vertical/perpendicular. // CvRect rect = ((CvContour *)contour)->rect; int A = rect.width / 2; int B = rect.height / 2; double estimated_area = M_PI * A * B; double error = fabs(actual_area - estimated_area); if (error > MAX_TOL) continue; printf ( "center x: %dy: %d A: %d B: %d\n", rect.x + A, rect.y + B, A, B ); CvScalar color = CV_RGB( rand() % 255, rand() % 255, rand() % 255 ); cvDrawContours( dst, contour, color, color, -1, CV_FILLED, 8, cvPoint(0,0)); } cvSaveImage("coins.png", dst, 0); } } 

Compte tenu de l’image binary fournie par Carnieri, voici la sortie:

 ./opencv-contour.out coin-ohtsu.pbm center x: 291 y: 328 A: 54 B: 42 center x: 286 y: 225 A: 46 B: 32 center x: 471 y: 221 A: 48 B: 33 center x: 140 y: 210 A: 42 B: 28 center x: 419 y: 116 A: 32 B: 19 

Et c’est l’image de sortie:

pièces de monnaie

Ce que vous pourriez améliorer sur:

  • Gère les différentes orientations de l’ellipse (actuellement, je suppose que les axes sont perpendiculaires / horizontaux). Ce ne serait pas difficile à faire en utilisant des moments d’image.
  • Vérifier la convexité des objects (voir cvConvexityDefects )

Votre meilleure façon de distinguer les pièces des autres objects va probablement être la forme. Je ne peux penser à aucune autre caractéristique d’image de bas niveau (la couleur est évidemment absente). Donc, je peux penser à deux approches:

Détection d’objects traditionnels

Votre première tâche consiste à séparer les objects (pièces et non-pièces) de l’arrière-plan. La méthode d’Ohtsu, suggérée par Carnieri, fonctionnera bien ici. Vous semblez craindre que les images soient bipartites, mais je ne pense pas que ce soit un problème. Tant qu’il y a une quantité importante de bureau visible, vous avez la garantie d’avoir un pic dans votre histogramme. Et tant qu’il y a quelques objects visuellement distinguables sur le bureau, vous êtes assuré de votre deuxième pic.

Dilatez votre image binary plusieurs fois pour éliminer le bruit laissé par le seuillage. Les pièces sont relativement grandes, elles doivent donc survivre à cette opération morphologique.

Regroupez les pixels blancs en objects à l’aide de la région croissante – connectez simplement les pixels de premier plan adjacents de manière itérative. À la fin de cette opération, vous aurez une liste d’objects disjoints et vous saurez quels pixels sont occupés par chaque object.

A partir de cette information, vous connaîtrez la largeur et la hauteur de l’object (à partir de l’étape précédente). Donc, maintenant vous pouvez estimer la taille de l’ellipse qui entourerait l’object, et ensuite voir à quel point cet object particulier correspond à l’ellipse. Il peut être plus simple d’utiliser simplement le rapport largeur / hauteur.

Vous pouvez également utiliser des moments pour déterminer la forme de l’object de manière plus précise.

Je ne sais pas quelle est la meilleure méthode pour votre problème. À propos du seuillage en particulier, vous pouvez utiliser la méthode d’Otsu, qui recherche automatiquement la valeur de seuil optimale en fonction d’une parsing de l’histogramme de l’image. Utilisez la méthode de seuil OpenCV avec le paramètre ThresholdType égal à THRESH_OTSU .

Sachez cependant que la méthode d’Otsu ne fonctionne bien que dans les images avec des histogrammes bimodaux (par exemple, des images avec des objects shinys sur un arrière-plan sombre).

Vous l’avez probablement déjà vu, mais il existe également une méthode pour ajuster une ellipse autour d’un ensemble de points 2D (par exemple, un composant connecté).

EDIT : la méthode d’Otsu appliquée à un exemple d’image:

Image en niveaux de gris: image en niveaux de gris

Résultat de l’application de la méthode d’Otsu: Otsu image

Si quelqu’un d’autre vient avec ce problème à l’avenir, mais en utilisant C ++:

Une fois que vous avez utilisé findContours pour trouver les contours (comme dans la réponse de Misha ci-dessus), vous pouvez facilement adapter les ellipses à l’aide de fitEllipse , par exemple

  vector > contours; findContours(img, contours, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0,0)); RotatedRect rotRecs[contours.size()]; for (int i = 0; i < contours.size(); i++) { rotRecs[i] = fitEllipse(contours[i]); }