跳转至

算法原理

关于分水岭算法的介绍和原理,大家可以去看这篇博客,上面有很多动图,十分生动有趣:https://www.cnblogs.com/mikewolf2002/p/3304118.html。我们,先来看一下opencv提供的分水岭算法的函数接口

void watershed( InputArray image, InputOutputArray markers ); 
输入图像必须是3通道的RGB图像,最为关键的是第二个参数markers,这个参数包含不同区域的轮廓,每个轮廓有一个自己唯一的编号,轮廓的定位可以通过Opencv中findContours方法实现。然后分水岭算法会根据markers传入的轮廓作为种子(也就是所谓的注水点),对图像上其他的像素点根据分水岭算法规则进行判断,并对每个像素点的区域归属进行划定,直到处理完图像上所有像素点。而区域与区域之间的分界处的值被置为“-1”,以做区分。

算法步骤

参考:https://blog.csdn.net/kakiebu/article/details/82965629

  • 将RGB图像灰度化
  • 使用大津法转为二值图,并做形态学闭合操作
  • 形态学闭操作
  • 距离变换
  • 将距离变换结果归一化到[0-1]之间
  • 将图像取值范围变为8位(0-255)
  • 再使用大津法转为二值图,并做形态学闭合操作
  • 使用findContours寻找marks
  • 对原图做形态学的腐蚀操作
  • 执行分水岭算法
  • 随机分配颜色和显示

代码实现

//分水岭算法
Mat WaterSegment(Mat src) {
    int row = src.rows;
    int col = src.cols;
    //1. 将RGB图像灰度化
    Mat grayImage = speed_rgb2gray(src);
    //2. 使用大津法转为二值图,并做形态学闭合操作
    threshold(grayImage, grayImage, 0, 255, THRESH_BINARY | THRESH_OTSU);
    //3. 形态学闭操作
    Mat kernel = getStructuringElement(MORPH_RECT, Size(9, 9), Point(-1, -1));
    morphologyEx(grayImage, grayImage, MORPH_CLOSE, kernel);
    //4. 距离变换
    distanceTransform(grayImage, grayImage, DIST_L2, DIST_MASK_3, 5);
    //5. 将图像归一化到[0, 1]范围
    normalize(grayImage, grayImage, 0, 1, NORM_MINMAX);
    //6. 将图像取值范围变为8位(0-255)
    grayImage.convertTo(grayImage, CV_8UC1);
    //7. 再使用大津法转为二值图,并做形态学闭合操作
    threshold(grayImage, grayImage, 0, 255, THRESH_BINARY | THRESH_OTSU);
    morphologyEx(grayImage, grayImage, MORPH_CLOSE, kernel);
    //8. 使用findContours寻找marks
    vector<vector<Point>> contours;
    findContours(grayImage, contours, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(-1, -1));
    Mat marks = Mat::zeros(grayImage.size(), CV_32SC1);
    for (size_t i = 0; i < contours.size(); i++)
    {
        //static_cast<int>(i+1)是为了分水岭的标记不同,区域1、2、3...这样才能分割
        drawContours(marks, contours, static_cast<int>(i), Scalar::all(static_cast<int>(i + 1)), 2);
    }
    //9. 对原图做形态学的腐蚀操作
    Mat k = getStructuringElement(MORPH_RECT, Size(3, 3), Point(-1, -1));
    morphologyEx(src, src, MORPH_ERODE, k);
    //10. 调用opencv的分水岭算法
    watershed(src, marks);
    //11. 随机分配颜色
    vector<Vec3b> colors;
    for (size_t i = 0; i < contours.size(); i++) {
        int r = theRNG().uniform(0, 255);
        int g = theRNG().uniform(0, 255);
        int b = theRNG().uniform(0, 255);
        colors.push_back(Vec3b((uchar)b, (uchar)g, (uchar)r));
    }

    // 12. 显示
    Mat dst = Mat::zeros(marks.size(), CV_8UC3);
    int index = 0;
    for (int i = 0; i < row; i++) {
        for (int j = 0; j < col; j++) {
            index = marks.at<int>(i, j);
            if (index > 0 && index <= contours.size()) {
                dst.at<Vec3b>(i, j) = colors[index - 1];
            }
            else if (index == -1)
            {
                dst.at<Vec3b>(i, j) = Vec3b(255, 255, 255);
            }
            else {
                dst.at<Vec3b>(i, j) = Vec3b(0, 0, 0);
            }
        }
    }
    return dst;
}

算法效果

原图

结果图

参考博客

https://www.cnblogs.com/wjy-lulu/p/7056466.html