算法原理

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