Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Fill the holes in emgu cv

Tags:

c#

emgucv

How can I fill the holes in binary image in emgu cv? In Aforge.net it's easy, use Fillholes class.

like image 357
Reza Liaghat Avatar asked Aug 26 '11 12:08

Reza Liaghat


People also ask

How do I fill a hole in an image in OpenCV?

Steps for implementing imfill in OpenCVThreshold the input image to obtain a binary image. Flood fill from pixel (0, 0). Notice the difference between the outputs of step 2 and step 3 is that the background in step 3 is now white. Invert the flood filled image ( i.e. black becomes white and white becomes black ).

What is hole filling in image processing?

The first hole-filling method is to fill in the holes in the depth image captured from the sensors used for rendering. Background-oriented priority is proposed to determine the filling order, and a fusing method for finding patches to fill in a hole based on the color and depth information is also proposed.


3 Answers

Thought the question is a little bit old, I'd like to contribute an alternative solution to the problem.

You can obtain the same result as Chris' without memory problem if you use the following:

private Image<Gray,byte> FillHoles(Image<Gray,byte> image)
    {
        var resultImage = image.CopyBlank();
        Gray gray = new Gray(255);
        using (var mem = new MemStorage())
        {
            for (var contour = image.FindContours(
                CHAIN_APPROX_METHOD.CV_CHAIN_APPROX_SIMPLE, 
                RETR_TYPE.CV_RETR_CCOMP, 
                mem); contour!= null; contour = contour.HNext)
            {
                resultImage.Draw(contour, gray, -1);
            }
        }

        return resultImage;
    }

The good thing about the method above is that you can selectively fill holes that meets your criteria. For example, you may want to fill holes whose pixel count (count of black pixels inside the blob) is below 50, etc.

private Image<Gray,byte> FillHoles(Image<Gray,byte> image, int minArea, int maxArea)
    {
        var resultImage = image.CopyBlank();
        Gray gray = new Gray(255);

        using (var mem = new MemStorage())
        {
            for (var contour = image.FindContours(
                CHAIN_APPROX_METHOD.CV_CHAIN_APPROX_SIMPLE, 
                RETR_TYPE.CV_RETR_CCOMP, 
                mem); contour!= null; contour = contour.HNext)
            {
                if ( (contour.Area < maxArea) && (contour.Area > minArea) )
                    resultImage.Draw(contour, gray, -1);
            }
        }

        return resultImage;
    }
like image 190
Oliver Avatar answered Sep 23 '22 05:09

Oliver


Yes there is a method but it's a bit messy as its based on cvFloodFill operation. Now all this algorithm is designed to do is fill an area with a colour until it reaches an edge similar to a region growing algorithm. To use this effectively you need to use a little inventive coding but I warn you this code is only to get you started it may require re-factoring to speed things up . As it stands the loop goes through each of your pixels that are less then 255 applies cvFloodFill checks what size the area is and then if it is under a certain area fill it in.

It is important to note that a copy of the image is made of the original image to be supplied to the cvFloodFill operation as a pointer is used. If the direct image is supplied then you will end up with a white image.

OpenFileDialog OpenFile = new OpenFileDialog();

if (OpenFileDialog.ShowDialog() == DialogResult.OK)
{
    Image<Bgr, byte> image = new Image<Bgr, byte>(OpenFile.FileName);

            for (int i = 0; i < image.Width; i++)
            {
                for (int j = 0; j < image.Height; j++)
                {
                    if (image.Data[j, i, 0] != 255)
                    {
                        Image<Bgr, byte> image_copy = image.Copy();
                        Image<Gray, byte> mask = new Image<Gray, byte>(image.Width + 2, image.Height + 2);
                        MCvConnectedComp comp = new MCvConnectedComp();
                        Point point1 = new Point(i, j);
                        //CvInvoke.cvFloodFill(
                        CvInvoke.cvFloodFill(image_copy.Ptr, point1, new MCvScalar(255, 255, 255, 255),
                        new MCvScalar(0, 0, 0),
                        new MCvScalar(0, 0, 0), out comp,
                        Emgu.CV.CvEnum.CONNECTIVITY.EIGHT_CONNECTED,
                        Emgu.CV.CvEnum.FLOODFILL_FLAG.DEFAULT, mask.Ptr);
                        if (comp.area < 10000)
                        {
                            image = image_copy.Copy();
                        }
                    }
                }
            }
}

The "new MCvScalar(0, 0, 0), new MCvScalar(0, 0, 0)," are not really important in this case as you are only filling in results of a binary image. YOu could play around with other settings to see what results you can achieve. "if (comp.area < 10000)" is the key constant to change is you want to change what size hole the method will fill.

These are the results that you can expect:

Original

Original Image

Results

The Resultant Image

The problem with this method is it's extremely memory intensive and it managed to eat up 6GB of ram on a 200x200 image and when I tried 200x300 it ate all 8GB of my RAM and brought everything to a crashing halt. Unless a majority of your image is white and you want to fill in tiny gaps or you can minimise where you apply the method I would avoid it. I would suggest writing you own class to examine each pixel that is not 255 and add the number of pixels surrounding it. You can then record the position of each pixel that was not 255 (in a simple list) and if your count was bellow a threshold set these positions to 255 in your images (by iterating though the list).

I would stick with the Aforge FillHoles class if you do not wish to write your own as it is designed for this purpose.

Cheers

Chris

like image 38
Chris Avatar answered Sep 23 '22 05:09

Chris


you can use FillConvexPoly

image.FillConvexPoly(externalContours.ToArray(), new Gray(255));
like image 25
hannes neukermans Avatar answered Sep 21 '22 05:09

hannes neukermans