web-dev-qa-db-fra.com

Remplissez les trous dans OpenCV

J'ai une carte Edge extraite du module de détection de Edge dans OpenCV (détection de bord aléatoire). Ce que je veux faire est de combler les trous dans la carte Edge. 

J'utilise les bibliothèques C++ et OpenCV. Dans OpenCV, la fonction cvFloodFill () remplit les trous avec une graine (avec l’un des emplacements pour commencer l’inondation). Cependant, j'essaie de combler tous les trous intérieurs sans connaître les graines. (Semblable à imfill () dans MATLAB)

Q1: comment trouver toutes les graines pour pouvoir appliquer 'cvFloodFill ()'?
Q2: comment implémenter un équivalent 'imfill ()'?

Débutant dans OpenCV, et tout indice est apprécié.

32
Lily

Selon la documentation de imfill in MATLAB:

BW2 = imfill(BW,'holes');

remplit les trous dans l'image binaire BW. Un trou est un ensemble de pixels d'arrière-plan impossible à atteindre en remplissant l'arrière-plan à partir du bord de l'image.

Par conséquent, pour obtenir les pixels "trous", appelez cvFloodFill avec le pixel du coin gauche de l'image comme germe. Vous obtenez les trous en complétant l'image obtenue à l'étape précédente.

Exemple MATLAB:

BW = im2bw( imread('coins.png') );
subplot(121), imshow(BW)

% used here as if it was cvFloodFill
holes = imfill(BW, [1 1]);    % [1 1] is the starting location point

BW(~holes) = 1;               % fill holes
subplot(122), imshow(BW)

 screenshot1  screenshot2

34
Amro

la fonction cvDrawContours a une option pour remplir les contours que vous avez dessinés.

Voici un court exemple cvDrawContours (IplImage, contours, couleur, couleur, -1, CV_FILLED, 8);

Voici la documentation

http://opencv.willowgarage.com/documentation/drawing_functions.html?highlight=cvdrawcontours#cvDrawContours

Je suppose que vous avez posté cela il y a longtemps, mais j'espère que cela aidera quelqu'un.

C'est le code source (en C #):

        Image<Gray, byte> image = new Image<Gray, byte>(@"D:\final.bmp");
        CvInvoke.cvShowImage("image 1", image);

        var contours = image.FindContours();
        while (contours != null)
        {
            CvInvoke.cvDrawContours(image, contours, new Gray(255).MCvScalar, new Gray (255).MCvScalar, 0, -1, Emgu.CV.CvEnum.LINE_TYPE.CV_AA, new DPoint(0, 0));
            contours = contours.HNext;
        }
        CvInvoke.cvShowImage("image 2", image);

Result

12
NotNamedDwayne

Je suis allé sur Internet pour trouver une fonction imfill appropriée (comme celle de Matlab) mais fonctionnant en C++ avec OpenCV. Après quelques recherches, 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 cela: Image binaire originale

Le résultat est: Image binaire finale

L'astuce consiste à définir les paramètres de la fonction cvDrawContours: CvDrawContours (dst, contour, white, white, 0, CV_FILLED);

  • dst = image de destination
  • contour = pointeur sur le premier contour
  • blanc = couleur utilisée pour remplir le contour
  • 0 = niveau maximal pour les contours dessinés. Si 0, seul le contour est dessiné
  • CV_FILLED = Epaisseur des lignes avec lesquelles les contours sont dessinés. S'il est négatif (par exemple, = CV_FILLED), les intérieurs de contour sont dessinés.

Plus d'informations dans la documentation openCV.

Il existe probablement un moyen d'obtenir "dst" directement sous forme d'image binaire, mais je n'ai pas trouvé comment utiliser la fonction cvDrawContours avec des valeurs binaires. 

7
Jeremy.S

J'ai créé une fonction simple qui équivaut à imfill ('trous') de matlab. Je ne l'ai pas testé dans de nombreux cas, mais cela a fonctionné jusqu'à présent. Je l'utilise sur des images Edge, mais il accepte tout type d'image binaire, comme lors d'une opération de seuillage.

Un trou n'est pas plus qu'un ensemble de pixels qui ne peuvent pas être "atteints" quand le fond est rempli, alors,

void fillEdgeImage(cv::Mat edgesIn, cv::Mat& filledEdgesOut) const
{
    cv::Mat edgesNeg = edgesIn.clone();

    cv::floodFill(edgesNeg, cv::Point(0,0), CV_RGB(255,255,255));
    bitwise_not(edgesNeg, edgesNeg);
    filledEdgesOut = (edgesNeg | edgesIn);

    return;
}

Voici un exemple de résultat

enter image description here

6
Pedro Batista

Voici une approche rapide et sale:

  1. Effectuez des manipulations sur votre image d’entrée de sorte que la nouvelle image binaire ait des 1 sur les bords et des 0 sinon
  2. Recherchez le premier 0 sur un côté de votre image Edge et lancez un remplissage avec des 1 à cet endroit sur une image vierge en utilisant votre image Edge comme masque. (Nous espérons ici que nous n'avons pas eu de malchance et que nous avons semé ce premier remplissage à l'intérieur d'une forme qui est à mi-distance de l'écran.)
  3. Cette nouvelle image inondée est le «fond». Tout pixel ayant un 1 est l’arrière-plan et tout pixel ayant un 0 est le premier plan.
  4. Faites une boucle dans l’image et recherchez les pixels de premier plan. Seed une inondation sur tout ce que vous trouvez. 
  5. OU cette nouvelle image inondée avec votre image Canny de l'étape 1 et vous avez terminé.
4
rcv

Juste une annexe pour la réponse de Amro.

void cvFillHoles(cv::Mat &input)
{
    //assume input is uint8 B & W (0 or 1)
    //this function imitates imfill(image,'hole')
    cv::Mat holes=input.clone();
    cv::floodFill(holes,cv::Point2i(0,0),cv::Scalar(1));
    for(int i=0;i<input.rows*input.cols;i++)
    {
        if(holes.data[i]==0)
            input.data[i]=1;
    }
}
3
Tae-Sung Shin

Récemment, je trouve aussi la solution à ce problème. Ici, j’ai implémenté l’idée de Amro :

#include <iostream>
using namespace std;
#include <cv.h>
#include <cxcore.h>
#include <highgui.h>
using namespace cv;

int main()
{
    IplImage *im = cvLoadImage("coin.png",CV_LOAD_IMAGE_ANYDEPTH);
    IplImage *hole = cvCreateImage(cvSize(im->width,im->height),8,1);
    cvShowImage("Original",im);

    cvCopyImage(im,hole);
    cvFloodFill(hole,cvPoint(0,0),cvScalar(255));
    cvShowImage("Hole",hole);
    cvSaveImage("hole.png",hole);

    cvNot(hole,hole);
    cvAdd(im,hole,im);
    cvShowImage("FillHole",im);
    cvSaveImage("fillHole.png",im);

    cvWaitKey(0);
    system("pause");
    return 0;
} 

J'espère que cela vous sera utile.

2
Bill Xia

Avez-vous essayé ContourFinding sur l’image Cannyied? 

cvFindContours crée une sorte d'arbre dans lequel les contours extérieurs sont les parents des contours intérieurs ("trous"). Voir échantillon contours.py. Des contours vous pouvez extraire des graines

2
dnul

Si vous avez les points sur les arêtes, vous pouvez utiliser fillConvexPoly () ou fillPoly () (si poly non convexe).

Une façon d'obtenir les points des arêtes est de faire findContours () -> approxPolyDP () .

0
Rui Marques