Watershed segmentation

Watershed is a segmentation method that is known for its efficiency. The method essentially starts from user-specified starting (seed) points from which regions grow. Assuming that good starting seeds can be provided, the resulting segmentations are useful for many purposes.

Note

For more details and examples about the watershed transform for image segmentation, see http://cmm.ensmp.fr/~beucher/wtshed.html.

The function watershed(InputArray image, InputOutputArray markers) accepts a 3-channel input image and an image called markers with the seeds. The latter has to be a 32-bit single-channel image. Seeds may be specified in markers as connected components with positive values (0 cannot be used as a value for seeds). As an output argument, each pixel in markers will be set to a value of the seed components or -1 at boundaries between the regions. OpenCV includes a watershed example ([opencv_source_code]/samples/cpp/watershed.cpp) in which the user has to draw the seed's regions.

Obviously, the selection of the seed regions is important. Ideally, seeds will be selected automatically without user intervention. A typical use of watershed is to first threshold the image to separate the object from the background, apply the distance transform, and then use the local maxima of the distance transform image as seed points for segmentation. However, the first thresholding step is critical, as parts of the object may be considered as the background. In this case, the object seed region will be too small and segmentation will be poor. On the other hand, to perform a watershed segmentation, we need seeds for the background too. While we can use points over the corners of the image as seeds, this will not be sufficient. In this case, the background seed region is too small. If we use those seeds, the object region given by the segmentation will be generally much larger than the real object. In our following watershed example, a different approach is followed that produces better results:

#include <opencv2/core/utility.hpp>
#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/core.hpp"
#include <iostream>

using namespace std;
using namespace cv;


void Watershed(const Mat &src)
{
    Mat dst=src.clone();

    // Flood fill outer part of the image
    Point seed(0,0); // top-left corner
    int loDiff=20;
    int upDiff=20;
    int flags=4 + FLOODFILL_FIXED_RANGE + FLOODFILL_MASK_ONLY + (255<<8);
    Mat mask(src.size(), CV_8UC1);
    mask.setTo(0);
    copyMakeBorder(mask, mask, 1, 1, 1, 1, cv::BORDER_REPLICATE);
    Scalar newVal;
    Rect ccomp;
    floodFill(dst, mask, seed, newVal, &ccomp,
         Scalar(loDiff, loDiff, loDiff), Scalar(upDiff, upDiff, upDiff), flags);

    // Flood fill inner part of the image
    seed.x=(float)src.cols/2;   // image center x
    seed.y=(float)src.rows/2;   // image center y
    Mat mask1=mask.clone();
    mask1.setTo(0);
    floodFill(dst, mask1, seed, newVal, &ccomp,
          Scalar(loDiff, loDiff, loDiff), Scalar(upDiff, upDiff, upDiff), flags);

    // Form image with the two seed regions
    Mat Mask = mask.clone();
    mask=mask/2;
    Mask = mask | mask1;
    imshow("Seed regions", Mask);
    moveWindow("Seed regions", src.cols, 0);

    // Perform watershed
    Mat labelImage(src.size(), CV_32SC1);
    labelImage=Mask(Rect(1,1, src.cols, src.rows));
    labelImage.convertTo(labelImage, CV_32SC1);
    watershed(src, labelImage);
    labelImage.convertTo(labelImage, CV_8U);
    imshow("Watershed", labelImage);
    moveWindow("Watershed", 2*src.cols, 0);
}


int main(int argc, char *argv[])
{
    // Read original image and clone it to contain results
    Mat src = imread("hand_sample2.jpg", IMREAD_COLOR );

    // Create 3 windows
    namedWindow("Source", WINDOW_AUTOSIZE);
    imshow("Source", src);

    Watershed(src);

    // Position windows on screen
    moveWindow("Source", 0,0);

    cout << "Press any key to exit...
";
    waitKey(); // Wait for key press
    return 0;
}

The Watershed function in the preceding code performs three steps. First, a background seed region is obtained by performing a flood fill. The flood fill seed is the upper left corner of the image, that is, pixel (0, 0). Next, another flood fill is performed to obtain an object's (hand in the sample image) seed region. The seed for this flood fill is taken as the center of the image. Then, a seed region image is formed by performing an OR operation between the previous two flood fill results. The resulting image is used as the seed image for the watershed operation. See the output of the example in the following screenshot where the seed image is shown at the center of the figure:

Watershed segmentation

The output of the watershed example

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset