c++opencvcontourwatershed

How to draw contours of each segmented object


I apply watershed segmentation to detect touching objects and it works okay doing that. Now, I would like to draw contours of each object, so I can get their length, area, moments etc.. But the objects in the result of the segmentation are still touching. So, I fail to draw contours of each one. How can I draw contours of each object?

#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <iostream>

using namespace std;
using namespace cv;

int main()
{
    Mat src = imread("source.png");

    // Create binary image from source image
    Mat srcGray;
    cvtColor(src, srcGray, CV_BGR2GRAY);

    Mat srcThresh;
    threshold(srcGray, srcThresh, 0, 255, CV_THRESH_BINARY | CV_THRESH_OTSU);

    // Perform the distance transform algorithm
    Mat dist;
    distanceTransform(srcThresh, dist, CV_DIST_L2, 3);

    // Normalize the distance image for range = {0.0, 1.0}
    normalize(dist, dist, 0, 1., NORM_MINMAX);

    // Threshold to obtain the peaks 
    threshold(dist, dist, 0.1, 3.5, CV_THRESH_BINARY);

    // Create the CV_8U version of the distance image
    Mat dist_8u;
    dist.convertTo(dist_8u, CV_8U);

    // Find total markers
    std::vector<std::vector<Point> > contours;
    findContours(dist_8u, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);
    int ncomp = contours.size();

    // Create the marker image for the watershed algorithm
    Mat markers = Mat::zeros(dist.size(), CV_32SC1);

    // Draw the foreground markers
    for (int i = 0; i < ncomp; i++)
        drawContours(markers, contours, i, Scalar::all(i + 1), -1);

    // Draw the background marker
    circle(markers, Point(5, 5), 3, CV_RGB(255, 255, 255), -1);

    // Perform the watershed algorithm
    watershed(src, markers);

    Mat wgResult = (markers.clone()) * 10000;

    imshow("Watershed", wgResult);

    waitKey(0);
    return 0;
}

Source image: enter image description here

Watershed Result: enter image description here


Solution

  • The markers matrix returned by watershed contains the indices of the segmented regions, according to the seed. So each component will have the same seed value. You can then create a binary matrix for each seed like:

    Mat1b mask = (markers == seed);
    

    Once you have the binary mask for each component, you can easily compute its area, moments, etc...

    Code:

    #include <opencv2/opencv.hpp>
    #include <iostream>
    using namespace std;
    using namespace cv;
    
    int main()
    {
        Mat src = imread("D:\\SO\\img\\postit.png");
    
        // Create binary image from source image
        Mat srcGray;
        cvtColor(src, srcGray, CV_BGR2GRAY);
    
        Mat srcThresh;
        threshold(srcGray, srcThresh, 0, 255, CV_THRESH_BINARY | CV_THRESH_OTSU);
    
        // Perform the distance transform algorithm
        Mat dist;
        distanceTransform(srcThresh, dist, CV_DIST_L2, 3);
    
        // Normalize the distance image for range = {0.0, 1.0}
        normalize(dist, dist, 0, 1., NORM_MINMAX);
    
        // Threshold to obtain the peaks 
        threshold(dist, dist, 0.1, 3.5, CV_THRESH_BINARY);
    
        // Create the CV_8U version of the distance image
        Mat dist_8u;
        dist.convertTo(dist_8u, CV_8U);
    
        // Find total markers
        std::vector<std::vector<Point> > contours;
        findContours(dist_8u, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);
        int ncomp = contours.size();
    
        // Create the marker image for the watershed algorithm
        Mat markers = Mat::zeros(dist.size(), CV_32SC1);
    
        // Draw the foreground markers
        for (int i = 0; i < ncomp; i++)
            drawContours(markers, contours, i, Scalar::all(i + 1), -1);
    
        // Draw the background marker
        circle(markers, Point(5, 5), 3, CV_RGB(255, 255, 255), -1);
    
        // Perform the watershed algorithm
        watershed(src, markers);
    
        for (int seed = 1; seed <= ncomp; ++seed)
        {
            Mat1b mask = (markers == seed);
    
            // Now you have the mask, you can compute your statistics
    
            imshow("Mask", mask);
            waitKey();
        }
    
        return 0;
    }