【问题标题】:OpenCV Watershed segmentation miss some objectsOpenCV 分水岭分割遗漏了一些对象
【发布时间】:2015-08-12 09:30:25
【问题描述】:

我的代码与tutorial 相同。 当我在使用cv::watershed() 后看到结果图像时,我想找出一个对象(右上角),但它不见了。 使用cv::drawContours()后,图像中确实有六个标记。 这是正常的,因为分水岭算法存在不准确吗?

这是我的代码的一部分:

Mat src = imread("result01.png");

Mat gray;
cvtColor(src, gray, COLOR_BGR2GRAY);

Mat thresh;
threshold(gray, thresh, 0, 255, THRESH_BINARY | THRESH_OTSU);

// noise removal
Mat kernel = Mat::ones(3, 3, CV_8UC1);
Mat opening;
morphologyEx(thresh, opening, MORPH_OPEN, kernel, Point(-1, -1), 2);

// Perform the distance transform algorithm
Mat dist_transform;
distanceTransform(opening, dist_transform, CV_DIST_L2, 5);

// Normalize the distance image for range = {0.0, 1.0}
// so we can visualize and threshold it
normalize(dist_transform, dist_transform, 0, 1., NORM_MINMAX);

// Threshold to obtain the peaks
// This will be the markers for the foreground objects
Mat dist_thresh;
threshold(dist_transform, dist_thresh, 0.5, 1., CV_THRESH_BINARY);

Mat dist_8u;
dist_thresh.convertTo(dist_8u, CV_8U);

// Find total markers
vector<vector<Point> > contours;
findContours(dist_8u, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);

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

// Draw the foreground markers
for (size_t i = 0; i < contours.size(); i++)
    drawContours(markers, contours, static_cast<int>(i), Scalar::all(static_cast<int>(i)+1), -1);

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

原图:

watershed 之后的结果:

您可以在此处找到原始图像、中间图像和结果图像:

Result images after specific process

【问题讨论】:

    标签: c++ opencv image-segmentation watershed


    【解决方案1】:

    在您的示例中,您认为 背景 被赋予与“缺失”对象相同的标签 (5)。

    您也可以通过将标签 (>0) 设置为背景来轻松调整它。 您可以找到确定背景膨胀和否定thresh 图像的内容。 然后,在创建标记时,您将标签定义为:

    • 0:未知
    • 1: 背景
    • &gt;1:你的对象

    在您的输出图像中,markers 将具有:

    • -1 : 对象之间的边缘
    • 0:背景(如watershed所指)
    • 1:背景(如你所定义)
    • &gt;1:你的对象。

    这段代码应该会有所帮助:

    #include <opencv2\opencv.hpp>
    #include <vector>
    
    using namespace std;
    using namespace cv;
    
    int main()
    {
        Mat3b src = imread("path_to_image");
    
        Mat1b gray;
        cvtColor(src, gray, COLOR_BGR2GRAY);
    
        Mat1b thresh;
        threshold(gray, thresh, 0, 255, THRESH_BINARY | THRESH_OTSU);
    
        // noise removal
        Mat1b kernel = getStructuringElement(MORPH_RECT, Size(3,3));
        Mat1b opening;
        morphologyEx(thresh, opening, MORPH_OPEN, kernel, Point(-1, -1), 2);
    
        Mat1b kernelb = getStructuringElement(MORPH_RECT, Size(21, 21));
        Mat1b background;
        morphologyEx(thresh, background, MORPH_DILATE, kernelb);
        background = ~background;
    
        // Perform the distance transform algorithm
        Mat1f dist_transform;
        distanceTransform(opening, dist_transform, CV_DIST_L2, 5);
    
        // Normalize the distance image for range = {0.0, 1.0}
        // so we can visualize and threshold it
        normalize(dist_transform, dist_transform, 0, 1., NORM_MINMAX);
    
        // Threshold to obtain the peaks
        // This will be the markers for the foreground objects
        Mat1f dist_thresh;
        threshold(dist_transform, dist_thresh, 0.5, 1., CV_THRESH_BINARY);
    
        Mat1b dist_8u;
        dist_thresh.convertTo(dist_8u, CV_8U);
    
        // Find total markers
        vector<vector<Point> > contours;
        findContours(dist_8u, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);
    
        // Create the marker image for the watershed algorithm
        Mat1i markers(dist_thresh.rows, dist_thresh.cols, int(0));
    
        // Background as 1
        Mat1i one(markers.rows, markers.cols, int(1));
        bitwise_or(one, markers, markers, background);
    
        // Draw the foreground markers (from 2 up)
        for (int i = 0; i < int(contours.size()); i++)
            drawContours(markers, contours, i, Scalar(i+2), -1);
    
        // Perform the watershed algorithm
        Mat3b dbg;
        cvtColor(opening, dbg, COLOR_GRAY2BGR);
        watershed(dbg, markers);
    
        Mat res;
        markers.convertTo(res, CV_8U);
        normalize(res, res, 0, 255, NORM_MINMAX);
    
        return 0;
    }
    

    结果:

    【讨论】:

    • 我非常接近最终结果。非常感谢。
    【解决方案2】:

    关于分水岭的 emgu 简历数据很少。 这是我使用 c# 对这个问题的翻译。我知道这不是正确的论坛,但这个答案突然出现在所有搜索者面前:

    //Mat3b src = imread("path_to_image");
    
    //cvtColor(src, gray, COLOR_BGR2GRAY);
    Image<Gray, byte> gray = smallImage.Convert<Gray, byte>();
    
    //threshold(gray, thresh, 0, 255, THRESH_BINARY | THRESH_OTSU);
    Image<Gray, byte> thresh = gray.ThresholdBinaryInv(new Gray(55), new Gray(255));
    
    // noise removal
    Mat kernel = CvInvoke.GetStructuringElement(ElementShape.Rectangle, new Size(3, 3), new Point(-1, -1));
    
    //Mat1b opening;
    //morphologyEx(thresh, opening, MORPH_OPEN, kernel, Point(-1, -1), 2);
    Image<Gray, byte> opening = thresh.MorphologyEx(MorphOp.Open, kernel, new Point(-1, -1), 2, BorderType.Default, new MCvScalar(255));
    
    //Mat1b kernelb = getStructuringElement(MORPH_RECT, Size(21, 21));
    Mat kernel1 = CvInvoke.GetStructuringElement(ElementShape.Rectangle, new Size(3, 3), new Point(-1, -1));
    //Mat1b background;
    //morphologyEx(thresh, background, MORPH_DILATE, kernelb);
    Image<Gray, byte> background = thresh.MorphologyEx(MorphOp.Dilate, kernel, new Point(-1, -1), 2, BorderType.Default, new MCvScalar(255));
    background = ~background;
    
    //// Perform the distance transform algorithm
    //Mat1f dist_transform;
    //distanceTransform(opening, dist_transform, CV_DIST_L2, 5);
    Image<Gray, float> dist_transform = new Image<Gray, float>(opening.Width, opening.Height);
    CvInvoke.DistanceTransform(opening, dist_transform, null, DistType.L2, 5);
    
    //// Normalize the distance image for range = {0.0, 1.0}
    //// so we can visualize and threshold it
    //normalize(dist_transform, dist_transform, 0, 1., NORM_MINMAX);
    CvInvoke.Normalize(dist_transform, dist_transform, 0, 1.0, NormType.MinMax, DepthType.Default);
    
    //// Threshold to obtain the peaks
    //// This will be the markers for the foreground objects
    //Mat1f dist_thresh;
    //threshold(dist_transform, dist_thresh, 0.5, 1., CV_THRESH_BINARY);
    Image<Gray, float> dist_thresh = new Image<Gray, float>(opening.Width, opening.Height);
    CvInvoke.Threshold(dist_transform, dist_thresh, 0.5, 1.0, ThresholdType.Binary);
    
    //Mat1b dist_8u;
    //dist_thresh.convertTo(dist_8u, CV_8U);
    Image<Gray, Byte> dist_8u = dist_thresh.Convert<Gray, Byte>();
    
    //// Find total markers
    //vector<vector<Point>> contours;
    //findContours(dist_8u, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);                
    VectorOfVectorOfPoint contours = new VectorOfVectorOfPoint();
    CvInvoke.FindContours(dist_8u, contours, null, RetrType.External, ChainApproxMethod.ChainApproxSimple);
    
    //// Create the marker image for the watershed algorithm
    //Mat1i markers(dist_thresh.rows, dist_thresh.cols, int(0));
    Image<Gray, int> markers = new Image<Gray, int>(dist_thresh.Width, dist_thresh.Height, new Gray(0));
    
    //// Background as 1
    //Mat1i one(markers.rows, markers.cols, int(1));
    //bitwise_or(one, markers, markers, background);
    Image<Gray, int> one = new Image<Gray, int>(markers.Cols, markers.Rows, new Gray(1));
    CvInvoke.BitwiseOr(one, markers, markers, background);
    
    //// Draw the foreground markers (from 2 up)
    for (int i = 0; i < contours.Size; i++)
    //  drawContours(markers, contours, i, Scalar(i + 2), -1);
        CvInvoke.DrawContours(markers, contours, i, new MCvScalar(i + 2));
    
    //// Perform the watershed algorithm
    //Mat3b dbg;
    //cvtColor(opening, dbg, COLOR_GRAY2BGR);
    //watershed(dbg, markers);
    Image<Bgr, byte> dbg = new Image<Bgr, byte>(markers.Cols, markers.Rows);
    CvInvoke.CvtColor(opening, dbg, ColorConversion.Gray2Bgr);
    CvInvoke.Watershed(dbg, markers);
    
    //Mat res;
    //markers.convertTo(res, CV_8U);
    //normalize(res, res, 0, 255, NORM_MINMAX);
    CvInvoke.Normalize(markers, markers, 0, 255, NormType.MinMax);
    
    //return 0;
    

    要在深色背景上查找浅色对象,请将 ThresholdBinaryInv 替换为 ThresholdBinary

    【讨论】:

      猜你喜欢
      • 2012-07-11
      • 2020-06-14
      • 1970-01-01
      • 1970-01-01
      • 2019-04-22
      • 2017-08-30
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多