Search code examples
c#emgucv

Fill the holes in emgu cv


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


Solution

  • 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