开发者

Fill the holes in OpenCV [duplicate]

开发者 https://www.devze.com 2022-12-11 17:25 出处:网络
This question alrea开发者_JAVA技巧dy has answers here: Filling holes inside a binary object (7 answers)
This question alrea开发者_JAVA技巧dy has answers here: Filling holes inside a binary object (7 answers) Closed 8 years ago.

I have an edge map extracted from edge detection module in OpenCV (canny edge detection). What I want to do is to fill the holes in the edge map.

I am using C++, and OpenCV libraries. In OpenCV there is a cvFloodFill() function, and it will fill the holes with a seed (with one of the location to start flooding). However, I am trying to fill all the interior holes without knowing the seeds.(similar to imfill() in MATLAB)

Q1: how to find all the seeds, so that I could apply 'cvFloodFill()'?

Q2: how to implement a 'imfill()' equivalent?

Newbie in OpenCV, and any hint is appreciated.


According to the documentation of imfill in MATLAB:

BW2 = imfill(BW,'holes');

fills holes in the binary image BW. A hole is a set of background pixels that cannot be reached by filling in the background from the edge of the image.

Therefore to get the "holes" pixels, make a call to cvFloodFill with the left corner pixel of the image as a seed. You get the holes by complementing the image obtained in the previous step.

MATLAB Example:

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)

Fill the holes in OpenCV [duplicate]

Fill the holes in OpenCV [duplicate]


the cvDrawContours function has an option to fill the contours that you have drawn.

Here is a short example cvDrawContours( IplImage, contours, color, color, -1, CV_FILLED, 8 );

Here is the documentation

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

I guess you posted this a long time ago, but I hope it helps someone.

This is the source code (in 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);

Fill the holes in OpenCV [duplicate]


I've been looking around the internet to find a proper imfill function (as the one in Matlab) but working in C++ with OpenCV. After some reaserches, I finally came up with a 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;
}

For this: Original Binary Image

Result is: Final Binary Image

The trick is in the parameters setting of the cvDrawContours function: cvDrawContours( dst, contour, white, white, 0, CV_FILLED);

  • dst = destination image
  • contour = pointer to the first contour
  • white = color used to fill the contour
  • 0 = Maximal level for drawn contours. If 0, only contour is drawn
  • CV_FILLED = Thickness of lines the contours are drawn with. If it is negative (For example, =CV_FILLED), the contour interiors are drawn.

More info in the openCV documentation.

There is probably a way to get "dst" directly as a binary image but I couldn't find how to use the cvDrawContours function with binary values.


I made a simple function that is equivalent to matlab's imfill('holes'). I've not tested it for many cases, but it has worked so far. I'm using it on edge images but it accepts any kind of binary image, like from a thresholding operation.

A hole is no more than a set of pixels that cannot be "reached" when background is filled, so,

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;
}

Here is an example result

Fill the holes in OpenCV [duplicate]


Here's a quick and dirty approach:

  1. Perform canny on your input image so that the new binary image has 1's at the edges, and 0's otherwise
  2. Find the first 0 along a side of your edge image, and initiate a floodfill with 1's at that point on a blank image using your edge image as the mask. (We're hoping here that we didn't get unlucky and seed this first fill on the inside of a shape that is half-off the screen)
  3. This new floodfilled image is the 'background'. Any pixel here that has a 1 is the background, and any pixel that has a 0 is the foreground.
  4. Loop through the image and find any foreground pixels. Seed a floodfill on any you find.
  5. OR this new floodfilled image with your Canny image from step 1, and you're done.


Just an appendix for Amro's answer.

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;
    }
}


Have you tried ContourFinding over the Cannyied Image?

cvFindContours creates sort of a tree in which the outer countours are parents to the inner contours ('holes'). See contours.py sample. From the contours you could extract seeds


Recently I'am also finding the solution to this problem. Here I implemented Amro 's idea as follows:

#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;
} 

Hope this will be helpful.


If you have the points from the edges you can use fillConvexPoly() or fillPoly() (if poly not convex).

One way to get the points from edges is to do findContours() -> approxPolyDP().

0

精彩评论

暂无评论...
验证码 换一张
取 消