44 votes

Détection de pièces (et de l'ajustement des points de suspension) sur une image

Je suis actuellement en train de travailler sur un projet où je suis en train d'essayer de détecter un peu d'argent posé sur une surface plane (un bureau). Les pièces de monnaie ne se chevauchent pas et ne sont pas masqués par d'autres objets. Mais il y a peut être d'autres objets visibles et les conditions d'éclairage ne peut pas être parfait... en fait considérer vous-même le tournage de votre bureau qui a quelques pièces de monnaie sur elle.

Ainsi, chaque point doit être visible sous la forme d'une Ellipse. Car je ne sais pas la position de la caméra de la forme de l'ellipse peut varier, d'un cercle (vue de haut), en plat ellipses en fonction de l'angle les pièces de monnaie sont filmées.

Mon problème est que je ne suis pas sûr de la manière d'extraire les pièces de monnaie et, enfin, l'ajustement des points de suspension au-dessus d'eux (dont je suis à la recherche pour faire d'autres calculs).

Pour l'instant j'ai juste fait une première tentative de définition d'une valeur de seuil de OpenCV, à l'aide de findContours() pour obtenir les lignes de contour et de côté d'une ellipse. Malheureusement, les lignes de contour que rarement de me donner la forme des pièces (réflexions, un mauvais éclairage, ...) et c'est pas préféré depuis que je ne veux pas l'utilisateur de définir un seuil.

Une autre idée est d'utiliser un modèle de méthode d'appariement d'une ellipse sur l'image, mais depuis que je ne connais pas l'angle de la caméra, ni la taille de l'ellipse, je ne pense pas que ce serait bien travailler...

Donc je voulais vous demander si quelqu'un pourrait me dire une méthode qui fonctionne dans mon cas...

Est-il un moyen rapide pour 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 devrait pas être trop sensible pour différentes ou lumières 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 pourrait fonctionner pour moi...

49voto

misha Points 10541

Voici quelques C99 source de la mise en œuvre de 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 qui Carnieri 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 c'est l'image de sortie:

coins

Ce que tu pourrais améliorer:

  • De gérer les différents ellipse orientations (actuellement, je suppose que les axes sont perpendiculaires à l'horizontal). Ce ne serait pas difficile de le faire à l'aide de l'image de moments.
  • Vérifiez pour objet de convexité (regardez cvConvexityDefects)

Votre meilleure manière de distinguer les pièces de monnaie à partir d'autres objets va probablement être en forme. Je ne peux pas penser à d'autres images de bas niveau des fonctions (la couleur est évidemment out). Donc, je pense à deux approches:

Traditionnelles de détection d'un objet

Votre première tâche consiste à séparer les objets (pièces de monnaie, et non des pièces) à partir de l'arrière-plan. Ohtsu de la méthode, comme suggéré par Carnieri, fonctionne bien 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 que il ya un couple de distinguables visuellement les objets sur le bureau, vous avez la garantie de votre deuxième pic.

Dilater votre image binaire d'un couple de fois pour se débarrasser du bruit de gauche par seuillage. Les pièces sont relativement grand, de sorte qu'ils doivent survivre à ce fonctionnement morphologique.

Groupe les pixels blancs dans les objets à l'aide de la région de la croissance-seulement de manière itérative connecter adjacentes de pixels d'avant-plan. À 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). Alors, 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 que d'utiliser la largeur vs ratio de hauteur.

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

6voto

carnieri Points 923

Je ne sais pas quelle est la meilleure méthode pour votre problème. À propos de seuillage spécifiquement, cependant, vous pouvez utiliser la méthode d'Otsu, qui trouve automatiquement la valeur du seuil optimal basé sur une analyse de l'histogramme de l'image. Utiliser OpenCV du seuil de la méthode avec le paramètre ThresholdType égal à THRESH_OTSU.

Soyez conscient, cependant, que Otsu la méthode de travail ainsi que dans les images avec bimodale histogrammes (par exemple, des images avec des objets lumineux sur un fond sombre).

Vous avez probablement déjà vu, mais il y a aussi une méthode pour le côté d'une ellipse autour d'un ensemble de points 2D (par exemple, un composant connecté).

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

Image en niveaux de gris: grayscale image

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

6voto

jamsandwich Points 11

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 de Misha réponse ci-dessus), vous pouvez facilement l'adapter à des ellipses à l'aide de 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]);
    }

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X