Fill the holes in OpenCV [duplicate]

2019-01-04 23:44发布

This question already has an answer here:

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.

9条回答
趁早两清
2楼-- · 2019-01-04 23:59

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)

screenshot1 screenshot2

查看更多
贼婆χ
3楼-- · 2019-01-05 00:00

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.
查看更多
Deceive 欺骗
4楼-- · 2019-01-05 00:03

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;
    }
}
查看更多
爱情/是我丢掉的垃圾
5楼-- · 2019-01-05 00:03

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().

查看更多
趁早两清
6楼-- · 2019-01-05 00:08

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

enter image description here

查看更多
劫难
7楼-- · 2019-01-05 00:12

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.

查看更多
登录 后发表回答