Monday 28 January 2013

Find Holes in a binary image

Since OpenCV is yet to provide special functions for blob analysis. I will show you a simple method of determining the number of holes in an image. This method exploits the ability of OpenCV's findContours() function to extract and distinguish between outer contour (shape boundaries) and inner contours (hole boundaries). For this tutorial, I assume the input image is a binary image. We will be using the 2 images below.


Figure 1: A) a complex shape with multiple levels of contours. B) a simple shape with 2 holes 

The result from the application are the following images

Figure 2:  Resulting images showing location of holes in A)  Figure 1A B) Figure 1B.

Code

Here is the full code:
#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>

using namespace cv;
using namespace std;

int main(int argc, char *argv[])
{
   if( argc != 2)
     {
       cout << " No image file is specified \n ";
       return -1;
     }

    Mat src = imread(argv[1]);

    vector<vector<Point> > contours;
    vector<Vec4i> hierarchy;

    findContours( src.clone(), contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_NONE );

    Mat singleLevelHoles = Mat::zeros(src.size(), src.type());
    Mat multipleLevelHoles = Mat::zeros(src.size(), src.type());


    for(vector<Vec4i>::size_type idx=0; idx<hierarchy.size(); ++idx)
    {
        if(hierarchy[idx][3] != -1)
           drawContours(singleLevelHoles, contours, idx, Scalar::all(255), CV_FILLED, 8, hierarchy);
    }

    bitwise_not(src, src);
    bitwise_and(src, singleLevelHoles, multipleLevelHoles);

    //Inverse source image.
    imwrite("/home/stephen/Pictures/Result0.jpg", src);

    //Holes before the bitwise AND operation.
    imwrite("/home/stephen/Pictures/Result1.jpg", singleLevelHoles);

    //Holes after the bitwise AND Operation.
    imwrite("/home/stephen/Pictures/Result2.jpg", multipleLevelHoles);

    return 0;
}

Explanation

  1. Lines 1-4: Standard includes from the OpenCV and C++ libraries.
  2. Lines 11-17: Error checking command line input. The image file path is passed in as command line input; the image is read into memory . 
  3. Lines 19-22: We extract contours along with the hierarchy information about the contours using the findContours() function in OpenCV. The contour retrieval mode is set to CV_RETR_TREE which simply retrieves the contour and reconstructs a full-hierarchy of nested contours. In other words, this mode causes the function to return a vector containing an entry for each contour; each entry is an array of 4 values. We are concerned with the last value of this array which tells us the parent of the contour. This value is set to -1 for outer contours otherwise it is set to the index of the parent contour in the hierarchy vector. For more information on contour hierarchy see here. The contour approximation method is  set to CV_CHAIN_APPROX_NONE which basically tells the function to store all contour points.
  4. Lines 28-32:  After extracting the contours, we use the hierarchy information to draw all the holes using the drawContours() function;  the thickness is set to CV_FILLED so that the drawn contours are filled with white pixels (255). This is perfect for when you do not have multiple levels of contours like Figure 1B but for the figure 1A the result would be as shown below.
    Figure 3: Resulting image showing holes in Figure 1A before applying logical operation.

  5. Lines 34-35: The result above could be undesirable for some operations; so the bitwise NOT and AND logical operations are used to get the location the inner contours of the image.

Conclusion

This is just a simple way to extract holes from an image without having to bother about extra libraries like cvBLobLib. Hope it was helpful. Happy Coding!

1 comment: