Ask Your Question
1

Selective hole filling

asked 2015-12-04 05:37:45 -0500

updated 2016-02-13 12:57:07 -0500

Hello everyone

At the moment I use cv::floodFill to fill holes in binary images. So, if I have the following image (These images are examples produced with a painting tool).

image description

After using cv::floodFill I get:

image description

However, I'd like to know if there is any way to get this instead:

image description

I can't think of a possible solution, so this is a shot in the dark.. no harm in trying though :)

Best regards!

edit retag flag offensive close merge delete

Comments

1

@PedroBatista you can recover your old user account see

sturkmen gravatar imagesturkmen ( 2015-12-04 05:45:48 -0500 )edit

Cool, thanks :)

Pedro Batista gravatar imagePedro Batista ( 2015-12-04 05:55:23 -0500 )edit
1

How about watershed filling as explained in this tutorial? It would respect the outer boundaries and only fill up the inner circles.

StevenPuttemans gravatar imageStevenPuttemans ( 2015-12-04 06:12:47 -0500 )edit
1

The problem of watershed is that I cannot guarantee that I can get an individual seed for each individual object with distanceTransform+threshold (this example is really simple). which wouldn't be such a big issue if the results on a failed instance wouldn't be so unpredictable.

Other problem is that I am running on low CPU resources (Using cortex A9 boards to process a stream of visual data) and all the steps necessary to use watershed are just too heavy for my system capabilities.

Pedro Batista gravatar imagePedro Batista ( 2015-12-04 06:47:02 -0500 )edit

2 answers

Sort by ยป oldest newest most voted
6

answered 2015-12-04 12:59:00 -0500

pklab gravatar image

updated 2015-12-12 12:08:37 -0500

How do you define holes to be filled ? Using shape, size, position ?

EDIT: after user specification:

If there are multiple holes in a blob, verify if one of them is enclosed by the others, in which case do not fill

"is enclosed by the others" isn't so clear to me, btw my idea is to find a convex hull around holes from same blob and fill those holes that touch the hull. Easy to say, less easy to do...

Source image

image description

convex hull in red (single or double holes in blue)

image description

result

image description

here my implementation, some optimization could be done ... I hope it's useful

void FillHolesHull(const cv::Mat &src, cv::Mat &dstBw)
{
    string funName = "FillHolesHull";
    cv::Mat gray, bw, contourMask, hullMask;
#ifdef _DEBUG
    Mat dbg;
    src.copyTo(dbg);
#endif
    cv::cvtColor(src, gray, CV_BGR2GRAY);
    int thr = 200;
    bw = gray < 200;  //THRESH_BINARY_INV: holes in black
    dstBw = 255 - bw; //THRESH_BINARY

    //find contours
    contourMask = Mat::zeros(bw.size(), CV_8UC1);
    hullMask = Mat::zeros(bw.size(), CV_8UC1);
    vector<vector<cv::Point> > contours;
    vector<Vec4i> hierarchy;
    findContours(bw, contours, hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE);

    //for each contour create a vector of children
    //and draw the parents contour in its map
    map<int, vector<int> > children;
    int maskTh = 2; //IMPORTANT !!! use 2px to flow contour line into the hole
    for (int i = 0; i < contours.size(); i++)
    {
#ifdef _DEBUG
        //putText(dbg, to_string(i), contours[i][0], 1, 1, color);
#endif
        int child = hierarchy[i][2];
        int parent = hierarchy[i][3];

        if (parent < 0)
            continue;
        children[parent].push_back(i);
        drawContours(contourMask, contours, i, 255, maskTh);
    }

    //for each parent contour 
    map<int, vector<int>>::iterator it;
    for (it = children.begin(); it != children.end(); it++)
    {
        int idx = it->first;
        int cnt = it->second.size();
        //fill contours if have 1 or 2 children
        if ((cnt > 0) && (cnt < 3))
        {
            drawContours(dstBw, contours, idx, 0, CV_FILLED);
#ifdef _DEBUG
            drawContours(dbg, contours, idx, Scalar(255, 0, 0), CV_FILLED);
#endif
            continue;
        }
        // HERE WHEN 3 OR MORE CHILDREN

        // utility: list of children contours for this parent
        vector<int> *brothers = &(it->second); 

        // group all brothers as single contour
        vector<cv::Point> allHolesContour;
        for (int i = 0; i < brothers->size(); i++)
        {
            idx = brothers->at(i); //contour index for i-th brother
            allHolesContour.insert(allHolesContour.end(), contours[idx].begin(), contours[idx].end());
        }
        //create convex hull around all brothers
        vector<Point> hull;
        convexHull(allHolesContour, hull, false);
        //draw the hull in its mask
        cv::polylines(hullMask, hull, true, 255, maskTh);
#ifdef _DEBUG
        cv::polylines(dbg, hull, true, Scalar(0, 0, 255), 1);
#endif
    }

    // locate brothers that touch the convexhull
    // logical AND between thee convexhull and holes itself
    bitwise_and(hullMask, contourMask, contourMask);
    // cut out black pixel in the source 
    bitwise_and(dstBw, contourMask, contourMask);

    // use this mask as seed for flood fill
    uchar *pMask;
    int nr = contourMask.rows;
    int nc = contourMask.cols;
    for (int r = 0; r < nr; ++r)
    {
        pMask = contourMask.ptr<uchar>(r);
        for (int c = 0; c< nc; ++c)
        {
            if (pMask[c]>0)
                floodFill(dstBw, Point(c, r), 0);
        }
    }

    imshow(funName + ":SRC", src);
    imshow(funName + ":DST-BW", dstBw);
#ifdef _DEBUG
    imshow(funName ...
(more)
edit flag offensive delete link more

Comments

1

Brilliant out-of-the-box answer!

emiswelt gravatar imageemiswelt ( 2015-12-07 12:50:32 -0500 )edit

Nice comprehensive answer, a good guide for those who struggle to use these kind of operations. However, it doesn't apply to my problem, the size and shape of the holes is totally arbitrary.

Pedro Batista gravatar imagePedro Batista ( 2015-12-09 04:38:44 -0500 )edit

@Pedro Batista ok the size and shape of the holes is totally arbitrary but it should be a rule, a principle, a key, a specification, something ?!? What is it ?

pklab gravatar imagepklab ( 2015-12-09 11:49:10 -0500 )edit

I've been thinking about that, and thought that the specification that may work is the following:

-> If there are multiple holes in a blob, verify if one of them is enclosed by the others, in which case do not fill it.

Pedro Batista gravatar imagePedro Batista ( 2015-12-10 04:13:46 -0500 )edit

Check EDIT in my answer

pklab gravatar imagepklab ( 2015-12-11 06:54:09 -0500 )edit

Great idea! I'll give it a go, thanks :)

Pedro Batista gravatar imagePedro Batista ( 2015-12-14 04:19:58 -0500 )edit

You really went out of your way to write a good answer, congrats :)

Pedro Batista gravatar imagePedro Batista ( 2015-12-14 04:26:27 -0500 )edit

no obligation but rather I hope it's works! my endeavour in answering is nothing compared to the big effort of OpenCV authors and many other users here ;)

pklab gravatar imagepklab ( 2015-12-14 05:14:31 -0500 )edit
1

answered 2015-12-07 21:21:18 -0500

Brandon212 gravatar image

If the things you're trying to fill are consistently round as in your example, you can use findContours and use the returned hierarchy to only look at internal contours. Check them for circularity and only fill those that are circular enough.

edit flag offensive delete link more
Login/Signup to Answer

Question Tools

1 follower

Stats

Asked: 2015-12-04 05:37:45 -0500

Seen: 1,928 times

Last updated: Dec 12 '15