Remplir des trous à l'intérieur d'un objet binaire

j'ai un problème avec le remplissage des trous blancs à l'intérieur d'une pièce de monnaie noire de sorte que je ne peux avoir que 0-255 image binaire avec des pièces de monnaie noires remplies.. J'ai utilisé le filtre médian pour l'accomplir mais dans ce cas le pont de connexion entre les pièces se développe et il va impossible de les reconnaître après plusieurs fois d'érosion... Donc j'ai besoin d'une méthode simple comme floodFill dans opencv

Voici mon image avec des trous:

enter image description here

EDIT: la fonction floodfill like doit remplir les trous dans les gros composants sans demander X,Y coordonnées comme une graine...

EDIT: j'ai essayé d'utiliser la fonction cvDrawContours mais je ne remplis pas les contours à l'intérieur des plus grands.

Voici mon code:

        CvMemStorage mem = cvCreateMemStorage(0);
        CvSeq contours = new CvSeq();
        CvSeq ptr = new CvSeq();
        int sizeofCvContour = Loader.sizeof(CvContour.class);

        cvThreshold(gray, gray, 150, 255, CV_THRESH_BINARY_INV);

        int numOfContours = cvFindContours(gray, mem, contours, sizeofCvContour, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE);
        System.out.println("The num of contours: "+numOfContours); //prints 87, ok

        Random rand = new Random();
        for (ptr = contours; ptr != null; ptr = ptr.h_next()) {
            Color randomColor = new Color(rand.nextFloat(), rand.nextFloat(), rand.nextFloat());
            CvScalar color = CV_RGB( randomColor.getRed(), randomColor.getGreen(), randomColor.getBlue());
            cvDrawContours(gray, ptr, color, color, -1, CV_FILLED, 8);
        }
        CanvasFrame canvas6  = new CanvasFrame("drawContours");
        canvas6.showImage(gray);

résultat: (vous pouvez voir les trous noirs à l'intérieur de chaque pièce)

enter image description here

27
demandé sur Zaur Guliyev 2012-04-25 16:52:17

7 réponses

il y a deux méthodes pour faire cela:

1) Garniture De Contour:

inversez D'abord l'image,Trouvez les contours de l'image, remplissez-la de noir et inversez en arrière.

des = cv2.bitwise_not(gray)
contour,hier = cv2.findContours(des,cv2.RETR_CCOMP,cv2.CHAIN_APPROX_SIMPLE)

for cnt in contour:
    cv2.drawContours(des,[cnt],0,255,-1)

gray = cv2.bitwise_not(des)

image résultante:

enter image description here

2) Ouverture De L'Image:

kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(3,3))
res = cv2.morphologyEx(gray,cv2.MORPH_OPEN,kernel)

l'image résultante comme suit:

enter image description here

vous pouvez voir, il n'y a pas beaucoup de différence dans les deux cas.

NB : image en gris, tous les codes sont en OpenCV-Python

39
répondu Abid Rahman K 2012-04-25 14:33:10

une simple dilatation et érosion fermerait assez bien les brèches, j'imagine. Je pense que c'est peut-être ce que vous cherchez.

Une solution plus robuste serait de faire un bord de détecter sur l'ensemble de l'image, puis une transformation de hough pour les cercles. Un rapide Google montre qu'il ya des échantillons de code disponibles dans diverses langues pour la détection invariante de la taille des cercles à l'aide d'une transformation hough, donc espérons que vous donnera quelque chose pour continuer.

le l'avantage d'utiliser la transformation de hough est que l'algorithme vous donnera en fait une estimation de la taille et de l'emplacement de chaque cercle, de sorte que vous pouvez reconstruire une image idéale basée sur ce modèle. Il devrait également être très robuste à se chevaucher, en particulier compte tenu de la qualité de l'image d'entrée ici (c.-à-d. moins de se soucier des faux positifs, donc peut abaisser le seuil pour les résultats).

5
répondu Elias Vasylenko 2013-04-16 13:43:45

vous pourriez être à la recherche du Fillhole transformation , une application de reconstruction d'image morphologique.

cette transformation remplira les trous dans vos pièces, même si au prix de remplir également tous les trous entre les groupes de pièces adjacentes. Les solutions basées sur l'espace ou l'ouverture suggérées par les autres affiches vous donneront probablement de meilleurs résultats de reconnaissance de haut niveau.

4
répondu thiton 2018-09-27 17:01:14

Essayez d'utiliser cvFindContours() de la fonction. Vous pouvez l'utiliser pour trouver les composants connectés. Avec les bons paramètres, Cette fonction renvoie une liste avec les contours de chaque composant connecté.

Trouver les contours qui représentent un trou. Ensuite, utilisez cvDrawContours () pour remplir le contour choisi par la couleur du premier plan fermant ainsi les trous.

1
répondu bubble 2012-04-25 14:11:10

je pense que si les objets sont touchés ou bondés, il y aura des problèmes en utilisant les contours et l'ouverture de la morophologie mathématique. Au lieu de cela, la solution simple suivante est trouvée et testée. Il fonctionne très bien, et pas seulement pour ces images, mais aussi pour toutes les autres images.

voici les étapes (optimisées) comme vu dans http://blogs.mathworks.com/steve/2008/08/05/filling-small-holes /

let I : le image d'entrée

1. filled_I = floodfill(I). // fill every hole in the image.
2. inverted_I = invert(I)`.   
3. holes_I = filled_I AND inverted_I. // finds all holes 
4. cc_list = connectedcomponent(holes_I) // list of all connected component in holes_I.
5. holes_I = remove(cc_list,holes_I, smallholes_threshold_size) // remove all holes from holes_I having size > smallholes_threshold_size.
6. out_I = I OR holes_I. // fill only the small holes.

En bref, l'algorithme est de trouver tous les trous, enlever le plus gros puis écrire les petits que sur l'image originale.

1
répondu Faroq Al-tam 2013-03-19 11:37:27

j'ai cherché sur internet pour trouver une fonction imfill (comme celle de Matlab) mais travaillant en C avec OpenCV. Après quelques explications, j'ai finalement trouvé une solution:

IplImage* imfill(IplImage* src)
{
    CvScalar white = CV_RGB( 255, 255, 255 );

    IplImage* dst = cvCreateImage( cvGetSize(src), 8, 3);
    CvMemStorage* storage = cvCreateMemStorage(0);
    CvSeq* contour = 0;

    cvFindContours(src, storage, &contour, sizeof(CvContour), CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE );
    cvZero( dst );

    for( ; contour != 0; contour = contour->h_next )
    {
        cvDrawContours( dst, contour, white, white, 0, CV_FILLED);
    }

    IplImage* bin_imgFilled = cvCreateImage(cvGetSize(src), 8, 1);
    cvInRangeS(dst, white, white, bin_imgFilled);

    return bin_imgFilled;
}

pour ceci: image binaire originale

résultat: image binaire finale

le truc est dans le paramétrage des cvDrawContours fonction: cvDrawContours (dst, contour, white, white, 0, CV_FILLED);

  • dst = image de destination
  • contour = pointeur vers le premier contour
  • blanc = couleur utilisée pour remplir le contour
  • 0 = Niveau Maximal pour les contours tracés. Si 0, seul le contour est dessiné
  • CV_FILLED = Épaisseur des lignes, les contours sont dessinés avec de la. Si elle est négative (par exemple, = CV_FILLED), les intérieurs de contour sont dessinés.

plus d'informations dans la documentation openCV.

il y a probablement un moyen d'obtenir" dst " directement comme une image binaire, mais je n'ai pas pu trouver comment utiliser la fonction cvDrawContours avec des valeurs binaires.

1
répondu Jeremy.S 2015-04-24 15:32:31

dans le cas où quelqu'un est à la recherche de la mise en œuvre du RPC -

            std::vector<std::vector<cv::Point> > contours_vector;

            cv::findContours(input_image, contours_vector, CV_RETR_LIST, CV_CHAIN_APPROX_NONE);

            cv::Mat contourImage(input_image.size(), CV_8UC1, cv::Scalar(0));
            for ( ushort contour_index = 0; contour_index < contours_vector.size(); contour_index++) {
                cv::drawContours(contourImage, contours_vector, contour_index, cv::Scalar(255), -1);
            }

            cv::imshow("con", contourImage);
            cv::waitKey(0);

enter image description here

enter image description here

0
répondu infoclogged 2018-09-27 16:28:21