watershed segmentation opencv xcode

26,111

Solution 1

There's a couple of things that should be mentioned about your code:

  • Watershed expects the input and the output image to have the same size;
  • You probably want to get rid of the const parameters in the methods;
  • Notice that the result of watershed is actually markers and not image as your code suggests; About that, you need to grab the return of process()!

This is your code, with the fixes above:

// Usage: ./app input.jpg
#include "opencv2/opencv.hpp"
#include <string>

using namespace cv;
using namespace std;

class WatershedSegmenter{
private:
    cv::Mat markers;
public:
    void setMarkers(cv::Mat& markerImage)
    {
        markerImage.convertTo(markers, CV_32S);
    }

    cv::Mat process(cv::Mat &image)
    {
        cv::watershed(image, markers);
        markers.convertTo(markers,CV_8U);
        return markers;
    }
};


int main(int argc, char* argv[])
{
    cv::Mat image = cv::imread(argv[1]);
    cv::Mat binary;// = cv::imread(argv[2], 0);
    cv::cvtColor(image, binary, CV_BGR2GRAY);
    cv::threshold(binary, binary, 100, 255, THRESH_BINARY);

    imshow("originalimage", image);
    imshow("originalbinary", binary);

    // Eliminate noise and smaller objects
    cv::Mat fg;
    cv::erode(binary,fg,cv::Mat(),cv::Point(-1,-1),2);
    imshow("fg", fg);

    // Identify image pixels without objects
    cv::Mat bg;
    cv::dilate(binary,bg,cv::Mat(),cv::Point(-1,-1),3);
    cv::threshold(bg,bg,1, 128,cv::THRESH_BINARY_INV);
    imshow("bg", bg);

    // Create markers image
    cv::Mat markers(binary.size(),CV_8U,cv::Scalar(0));
    markers= fg+bg;
    imshow("markers", markers);

    // Create watershed segmentation object
    WatershedSegmenter segmenter;
    segmenter.setMarkers(markers);

    cv::Mat result = segmenter.process(image);
    result.convertTo(result,CV_8U);
    imshow("final_result", result);

    cv::waitKey(0);

    return 0;
}

I took the liberty of using Abid's input image for testing and this is what I got:

enter image description here

Solution 2

Below is the simplified version of your code, and it works fine for me. Check it out :

#include "opencv2/objdetect/objdetect.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"

using namespace cv;
using namespace std;

int main ()
{
    Mat image = imread("sofwatershed.jpg");
    Mat binary = imread("sofwsthresh.png",0);

    // Eliminate noise and smaller objects
    Mat fg;
    erode(binary,fg,Mat(),Point(-1,-1),2);

    // Identify image pixels without objects
    Mat bg;
    dilate(binary,bg,Mat(),Point(-1,-1),3);
    threshold(bg,bg,1,128,THRESH_BINARY_INV);

// Create markers image
    Mat markers(binary.size(),CV_8U,Scalar(0));
    markers= fg+bg;

markers.convertTo(markers, CV_32S);
watershed(image,markers);

markers.convertTo(markers,CV_8U);
imshow("a",markers);
waitKey(0);
}

Below is my input image :

enter image description here

Below is my output image :

enter image description here

See the code explanation here : Simple watershed Sample in OpenCV

Share:
26,111
Yaozhong
Author by

Yaozhong

Updated on May 16, 2020

Comments

  • Yaozhong
    Yaozhong about 4 years

    I am now learning a code from the opencv codebook (OpenCV 2 Computer Vision Application Programming Cookbook): Chapter 5, Segmenting images using watersheds, page 131.

    Here is my main code:

    #include "opencv2/opencv.hpp"
    #include <string>
    
    using namespace cv;
    using namespace std;
    
    class WatershedSegmenter {
        private:
        cv::Mat markers;
        public:
        void setMarkers(const cv::Mat& markerImage){
            markerImage.convertTo(markers, CV_32S);
        }
    
        cv::Mat process(const cv::Mat &image){
            cv::watershed(image,markers);
            return markers;
        }
    };
    
    int main ()
    {
        cv::Mat image = cv::imread("/Users/yaozhongsong/Pictures/IMG_1648.JPG");
    
        // Eliminate noise and smaller objects
        cv::Mat fg;
        cv::erode(binary,fg,cv::Mat(),cv::Point(-1,-1),6);
    
        // Identify image pixels without objects
        cv::Mat bg;
        cv::dilate(binary,bg,cv::Mat(),cv::Point(-1,-1),6);
        cv::threshold(bg,bg,1,128,cv::THRESH_BINARY_INV);
    
        // Create markers image
        cv::Mat markers(binary.size(),CV_8U,cv::Scalar(0));
        markers= fg+bg;
    
        // Create watershed segmentation object
        WatershedSegmenter segmenter;
        // Set markers and process
        segmenter.setMarkers(markers);
        segmenter.process(image);
    
        imshow("a",image);
        std::cout<<".";
        cv::waitKey(0);
    }
    

    However, it doesn't work. How could I initialize a binary image? And how could I make this segmentation code work?

    I am not very clear about this part of the book. Thanks in advance!

  • Yaozhong
    Yaozhong almost 12 years
    Thx! I have one question: what's the relationship between sofwatershed.jpg and sofwsthresh.png?
  • Abid Rahman K
    Abid Rahman K almost 12 years
    OH. i am sorry. sofwatershed.png is the original image. sofwsthresh.png is the thresholded image. Actually i used Python. So i didn't want to binarize it again in C++. You can find those images in the link i have provided at end.
  • Yaozhong
    Yaozhong almost 12 years
    I have a question, which image is your final segmentation image?is mat image?my image after the watershed function is the same as the original image...
  • Abid Rahman K
    Abid Rahman K almost 12 years
    second image is the result. Actually i am not at all good in C++. I use Python only.
  • karlphillip
    karlphillip almost 12 years
    @AbidRahmanK +1 I upvoted your answer for the initiative, but that won't show the OP his mistakes. So I added an answer as well.
  • Yaozhong
    Yaozhong almost 12 years
    cool!it works!the size of original image and binary image should be the same. Thx!
  • Abid Rahman K
    Abid Rahman K almost 12 years
    @karlphillip: yeah, i know it. But as usual, C++ is still a tragedy for me :)
  • karlphillip
    karlphillip over 6 years
    This is from 2012. OpenCV API may have changed a bit since then. What problems are you having?
  • Daniel
    Daniel almost 6 years
    @karlphillip I think he is referring to the fact that no segmentation has occurred?
  • karlphillip
    karlphillip almost 6 years
    @Daniel "Not working" is a poor definition of the problem. I can't help anyone without more technical information.
  • Daniel
    Daniel almost 6 years
    Yeah it's just that what you've provided is a completely incorrect application of watershed. I think it is clear what @KansaiRobot is saying when s/he says "it is not working". All that's given is a solution for background subtraction. As mmgp points out in the referenced thread, this is not the point of using watershed. Neither this answer nor Abid's should be upvoted in either thread, because both are wrong. A working watershed solution would segment the ROI, like so.
  • karlphillip
    karlphillip almost 6 years
    @Daniel Thanks for your comments. I would have never figured out all of that just from someone saying "It's not working". My first guess would be a compiler or a linker-related issue.
  • KansaiRobot
    KansaiRobot almost 6 years
    watershedding is separating the pills in the original image. Which the final result is not doing
  • karlphillip
    karlphillip almost 6 years
    Guys, I don't have access to the book and the OP also didn't ask help to segment pills individually. But more important than that, is that this answer was sufficient to help him solve his problem. There is no point on spending energy discussing an issue that was successfully closed 6 years ago. Now, If you can provide a better answer, please write it! As far as my choice to use watershed for something different, remember, watershed is just an algorithm. Computer algorithms have been used (for decades) to solve a multitude of problems that they weren't originally designed for.