web-dev-qa-db-fra.com

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

Je travaille actuellement sur un projet où j'essaie de détecter quelques pièces de monnaie reposant sur une surface plane (par exemple 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 ... Fondamentalement, pensez à filmer votre bureau qui contient des pièces de monnaie.

Ainsi, chaque point doit être visible comme une ellipse. Étant donné que je ne connais pas la position de la caméra, la forme des ellipses peut varier, d'un cercle (vue de dessus) à des ellipses plates selon l'angle à partir duquel les pièces sont filmées.

Mon problème est que je ne sais pas comment extraire les pièces de monnaie et finalement y placer des ellipses (que je recherche pour faire d'autres calculs).

Pour l'instant, je viens de faire la première tentative en définissant une valeur de seuil dans OpenCV, en utilisant findContours () pour obtenir les lignes de contour et en ajustant une ellipse. Malheureusement, les courbes de niveau ne me donnent que rarement la forme des pièces (reflets, mauvais éclairage, ...) et cette manière n'est également pas préférée car je ne veux pas que l'utilisateur fixe 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 ...

Je voulais donc 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 des appareils mobiles et la méthode ne doit pas être trop sensible à des lumières différentes ou changeantes ou à la couleur de l'arrière-plan. =

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

44
florianbaethge

Voici une source C99 implémentant 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);
    }
}

Compte tenu de l'image binaire 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 voici l'image de sortie:

coins

Ce que vous pourriez améliorer:

  • Gérer différentes orientations d'ellipse (actuellement, je suppose que les axes sont perpendiculaires/horizontaux). Ce ne serait pas difficile à faire en utilisant des moments d'image.
  • Vérifiez la convexité des objets (jetez un œil à cvConvexityDefects)

Votre meilleure façon de distinguer les pièces des autres objets sera probablement par la forme. Je ne peux penser à aucune autre fonctionnalité d'image de bas niveau (la couleur est évidemment absente). Je peux donc penser à deux approches:

Détection d'objets traditionnels

Votre première tâche consiste à 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, fonctionnera bien ici. Vous semblez vous inquiéter que les images soient bipartites mais je ne pense pas que ce sera un problème. Tant qu'il y a une quantité importante de bureau visible, vous êtes assuré d'avoir un pic dans votre histogramme. Et tant qu'il y a quelques objets visuellement reconnaissables sur le bureau, vous êtes assuré de votre deuxième pic.

Dilate votre image binaire plusieurs fois pour éliminer le bruit laissé par le seuillage. Les pièces sont relativement grosses et devraient donc survivre à cette opération morphologique.

Regroupez les pixels blancs en objets à l'aide de l'agrandissement de région - connectez simplement de manière itérative les pixels de premier plan adjacents. À la fin de cette opération, vous aurez une liste d'objets disjoints et vous saurez quels pixels chaque objet occupe.

A partir de ces informations, vous connaîtrez la largeur et la hauteur de l'objet (à partir de l'étape précédente). Ainsi, vous pouvez maintenant estimer la taille de l'ellipse qui entourerait l'objet, puis voir dans quelle mesure cet objet particulier correspond à l'ellipse. Il peut être plus facile d'utiliser simplement le rapport largeur/hauteur.

Alternativement, vous pouvez ensuite utiliser moments pour déterminer la forme de l'objet de manière plus précise.

49
mpenkov

Si quelqu'un d'autre vient avec ce problème à l'avenir comme je l'ai fait, 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 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
jamsandwich

Je ne sais pas quelle est la meilleure méthode pour votre problème. Cependant, en ce qui concerne le seuillage en particulier, vous pouvez utiliser la méthode d'Otsu, qui trouve automatiquement la valeur de seuil optimale en fonction d'une analyse de l'histogramme de l'image. Utilisez la méthode seuil d'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 objets lumineux sur un fond sombre).

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

[~ # ~] modifier [~ # ~] : la méthode d'Otsu appliquée à un exemple d'image:

Image en niveaux de gris: grayscale image

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

6
carnieri