算法原理
关于分水岭算法的介绍和原理,大家可以去看这篇博客,上面有很多动图,十分生动有趣: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;
}
算法效果
原图
结果图