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

je travaille actuellement sur un projet où j'essaie de détecter quelques pièces qui gisent sur une surface plane (c.-à-d. un bureau). Les pièces ne se chevauchent pas et ne sont pas cachées par d'autres objets. Mais il peut y avoir d'autres objets visibles et les conditions d'éclairage peuvent ne pas être parfaites... En gros, considère-toi en train de filmer ton bureau qui a des pièces de monnaie dessus.

ainsi chaque point doit être visible comme une Ellipse. Comme je ne connais pas la position de la caméra, la forme des ellipses peut varier., du cercle (vue du haut) 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 sur la façon d'extraire les pièces de monnaie et finalement adapter les ellipses sur eux (que je cherche à 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 courbes de niveau ne me donnent que rarement la forme de la pièces de monnaie (reflets, mauvais éclairage,...) et cette voie est également pas préféré depuis que je ne veux pas l'utilisateur de définir un seuil.

une autre idée était d'utiliser un modèle de méthode de correspondance 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 j'ai voulu demander si quelqu'un pouvait me dire une méthode qui fonctionnerait dans mon cas...

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

Serait génial si quelqu'un pouvait me donner des conseils sur la méthode pourrait fonctionner pour moi...

43
demandé sur evident 2011-01-24 21:05:44

3 réponses

voici quelques sources de C99 mettant en œuvre l'approche traditionnelle (basée sur OpenCV doco):

#include "cv.h"
#include "highgui.h"

#include <stdio.h>

#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: %d y: %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);
    }
}

étant donné l'image binaire que Carnieri a fourni, c'est 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 voici l'image de sortie:

coins

ce que vous pourriez améliorer sur:

  • gérer différentes orientations elliptiques (actuellement, je suppose que les axes sont perpendiculaires/horizontaux). Ce ne serait pas difficile à faire l'aide de l'image instants.
  • Vérifiez la convexité de l'objet (regardez cvConvexityDefects)

votre meilleure façon de distinguer les pièces des autres objets sera probablement par la forme. Je ne peux pas penser à d'autres traits d'image de bas niveau (la couleur est évidemment dehors). Donc, je peux penser à deux approches:

détection D'objets traditionnels

votre première tâche est de séparer les objets (pièces et non-pièces) de l'arrière-plan. La méthode d'Ohtsu, comme suggéré par Carnieri, va bien fonctionner ici. Vous semblez vous soucier de l'image bipartite mais je ne pense pas que ce sera 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 aussi longtemps qu'il y a quelques objets visuellement distinguables sur le bureau, vous êtes garanti votre deuxième pic.

Dilater votre image binaire quelques fois pour se débarrasser du bruit laissé par seuillage. Les pièces sont relativement grosses et devraient donc survivre à cette opération morphologique.

groupez les pixels blancs en objets en utilisant la zone de croissance -- connectez juste itérativement les pixels d'avant-plan adjacents. À la fin de cette opération, vous aurez une liste d'objets disjoints, et vous savez lequel les pixels de chaque objet occupe.

à Partir de cette information, vous permettra de connaître la largeur et la hauteur de l'objet (de l'étape précédente). Donc, maintenant vous pouvez estimer la taille de l'ellipse qui entourent l'objet, puis de voir comment cet objet correspond à l'ellipse. Il peut être plus facile d'utiliser le rapport largeur / hauteur.

Alternativement, vous pouvez utiliser moments pour déterminer la forme de l'objet de façon plus précise.

49
répondu mpenkov 2012-06-08 21:48:13

je ne sais pas quelle est la meilleure méthode pour votre problème. En ce qui concerne le battage, cependant, vous pouvez utiliser la méthode D'Otsu, qui trouve automatiquement la valeur de seuil optimale basée sur une analyse de l'histogramme de l'image. Utilisez Opencv's seuil méthode avec le paramètre ThresholdType égal THRESH_OTSU.

vous avez probablement vu cela, mais il y a aussi une méthode pour ajustement d'une ellipse autour d'un ensemble de points 2D (par exemple, un composant connecté).

EDIT: Otsu de la méthode appliquée à un échantillon de l'image:

image en échelle de gris: grayscale image

6
répondu carnieri 2011-01-24 23:23:49

Si quelqu'un d'autre vient avec ce problème à l'avenir comme je l'ai fait, mais à l'aide de C++:

une Fois que vous avez utilisé findContours pour trouver les contours (comme dans la réponse de Misha ci-dessus), vous pouvez facilement ajuster les ellipses en utilisant fitEllipse, par exemple

    vector<vector<Point> > 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]);
    }
6
répondu jamsandwich 2014-05-26 06:55:42