点击上方“小白学视觉”,选择“星标”公众号
重磅干货,第一时间送达

414ea2edea8c85be9a45f235e0e010f6.png

经过几个月的努力,小白终于完成了市面上第一本OpenCV 4入门书籍《OpenCV 4开发详解》。为了更让小伙伴更早的了解最新版的OpenCV 4,小白与出版社沟通,提前在公众号上连载部分内容,请持续关注小白。

分水岭算法与漫水填充法相似,都是模拟水淹过山地的场景,区别是漫水填充法是从局部某个像素值进行分割,是一种局部分割算法,而分水岭法是从全局出发,需要对全局都进行分割。

分水岭算法会在多个局部最低点开始注水,随着注水量的增加,水位越来越高会淹没局部像素值较小的像素点,最后两个相邻的凹陷区域的水会汇集在一起,并在汇集处形成了分水岭。分水岭的计算过程是一个迭代标注的过程,经典的计算方式主要分为以下两个步骤:

  • Step1:排序过程,首先对图像像素的灰度级进行排序,确定灰度值较小的像素点,该像素点即为开始注水点;
  • Step2:淹没过程,对每个最低点开始不断注水,不断掩模周围的像素点,不同注水处的水汇集在一起,形成分割线。

OpenCV 4提供了用于实现分水岭法分割图像的watershed()函数,该函数的函数原型在代码清单8-19中给出。

代码清单8-19 watershed()函数原型	void cv::watershed(InputArray  image,	                       InputOutputArray  markers 	                       )
  • image:输入图像,数据类型为CV_8U的三通道图像。
  • markers:输入/输出CV_32S的单通道图像的标记结果,与原图像具有相同的尺寸。

该函数根据期望标记结果实现图像分水岭分割。函数的第一个参数是需要进行分水岭分割的图像,该图像必须是CU_8U的三通道彩色图像。函数第二个参数用于输入期望分割的区域,在将图像传递给函数之前,必须使用大于0的整数索引粗略的勾画图像期望分割的区域。因此,每个标记的区域被表示为具有像素值1、2、3等的一个或多个连通分量。标记图像的尺寸与输入图像相同且数据类型为CV_32S,可以使用findContours()函数和drawContours()函数从二值掩码中得到此类标记图像,标记图像中所有没有被标记的像素值都为0。在函数输出时,两个区域之间的分割线用-1表示。

为了了解该函数的用法,在代码清单8-20中给出了利用watershed()函数对图像进行分割的示例程序。程序中通过图像的边缘区域对图像进行标记,首先利用Canny()函数计算图像的边缘,之后利用findContours()函数计算图像中的连通域,并通过drawContours()函数绘制连通域得到符合格式要求的标记图像,最后利用watershed()函数对图像进行分割。为了增加分割后不同区域之间的对比度,随机对不同区域进行上色,结果如图8-12所示,同时提取原图像中每个被分割的区域,部分结果在图8-13给出。

代码清单8-20 myWatershed.cpp分水岭法分割图像#include #include using namespace std;using namespace cv;int main(){	Mat img, imgGray, imgMask;	Mat maskWaterShed;  // watershed()函数的参数	img = imread("HoughLines.jpg");  //原图像	if (img.empty())	{		cout << "请确认图像文件名称是否正确" << endl;		return -1;	}	cvtColor(img, imgGray, COLOR_BGR2GRAY);	//GaussianBlur(imgGray, imgGray, Size(5, 5), 10, 20);  //模糊用于减少边缘数目	//提取边缘并进行闭运算	Canny(imgGray, imgMask, 150, 300);	//Mat k = getStructuringElement(0, Size(3, 3));	//morphologyEx(imgMask, imgMask, MORPH_CLOSE, k);	imshow("边缘图像", imgMask);	imshow("原图像", img);	//计算连通域数目	vector> contours;	vector hierarchy;	findContours(imgMask, contours, hierarchy, RETR_CCOMP, CHAIN_APPROX_SIMPLE);	//在maskWaterShed上绘制轮廓,用于输入分水岭算法	maskWaterShed = Mat::zeros(imgMask.size(), CV_32S);	for (int index = 0; index < contours.size(); index++)	{		drawContours(maskWaterShed, contours, index, Scalar::all(index + 1),			-1, 8, hierarchy, INT_MAX);	}	//分水岭算法   需要对原图像进行处理	watershed(img, maskWaterShed);	vector colors;  // 随机生成几种颜色	for (int i = 0; i < contours.size(); i++)	{		int b = theRNG().uniform(0, 255);		int g = theRNG().uniform(0, 255);		int r = theRNG().uniform(0, 255);		colors.push_back(Vec3b((uchar)b, (uchar)g, (uchar)r));	}	Mat resultImg = Mat(img.size(), CV_8UC3);  //显示图像	for (int i = 0; i < imgMask.rows; i++)	{		for (int j = 0; j < imgMask.cols; j++)		{			// 绘制每个区域的颜色			int index = maskWaterShed.at(i, j);			if (index == -1)  // 区域间的值被置为-1(边界)			{				resultImg.at(i, j) = Vec3b(255, 255, 255);			}			else if (index <= 0 || index > contours.size())  // 没有标记清楚的区域被置为0 			{				resultImg.at(i, j) = Vec3b(0, 0, 0);			}			else  // 其他每个区域的值保持不变:1,2,…,contours.size()			{				resultImg.at(i, j) = colors[index - 1];  // 把些区域绘制成不同颜色			}		}	}	resultImg = resultImg * 0.6 + img * 0.4;	imshow("分水岭结果", resultImg);	//绘制每个区域的图像	for (int n = 1; n <= contours.size(); n++)	{		Mat resImage1 = Mat(img.size(), CV_8UC3);  // 声明一个最后要显示的图像		for (int i = 0; i < imgMask.rows; i++)		{			for (int j = 0; j < imgMask.cols; j++)			{				int index = maskWaterShed.at(i, j);				if (index == n)					resImage1.at(i, j) = img.at(i, j);				else					resImage1.at(i, j) = Vec3b(0, 0, 0);			}		}		//显示图像		imshow(to_string(n), resImage1);	}	waitKey(0);	return 0;}
b2b6b44b9d1094904679c0da6a5ee295.png
图8-12 myWatershed.cpp程序中分水岭分割结果
40a50a9f915df035dc21c75684822d62.png
图8-13 myWatershed.cpp程序中被分割区域的原图像
从零学习OpenCV 4往期推荐

【从零学习OpenCV 4】Windows系统中安装OpenCV 4

【从零学习OpenCV 4】Ubuntu系统中安装OpenCV 4

【从零学习OpenCV 4】opencv_contrib扩展模块的安装

【从零学习OpenCV 4】Mat类介绍

【从零学习OpenCV 4】Mat类构造与赋值

【从零学习OpenCV 4】Mat类支持的运算

【从零学习OpenCV 4】这4种读取Mat类元素的的方法你都知道么?

【从零学习OpenCV 4】namedWindow函数&imshow函数的使用

【从零学习OpenCV 4】颜色模型与转换

【从零学习OpenCV 4】多通道分离与合并

【从零学习OpenCV 4】图像像素统计

【从零学习OpenCV 4】两图像间的像素操作

【从零学习OpenCV 4】图像二值化

【从零学习OpenCV 4】LUT查找表

【从零学习OpenCV 4】图像仿射变换

【从零学习OpenCV 4】图像透视变换

【从零学习OpenCV 4】极坐标变换

【从零学习OpenCV 4】绘制几何图形

【从零学习OpenCV 4】图像金字塔

【从零学习OpenCV 4】创建图像窗口滑动条

【从零学习OpenCV 4】鼠标响应

【从零学习OpenCV 4】图像直方图绘制

【从零学习OpenCV 4】直方图归一化

【从零学习OpenCV 4】直方图比较

【从零学习OpenCV 4】直方图均衡化

【从零学习OpenCV 4】直方图匹配

【从零学习OpenCV 4】图像卷积

【从零学习OpenCV 4】图像中添加椒盐噪声

【从零学习OpenCV 4】图像中添加高斯噪声

【从零学习OpenCV 4】均值滤波

【从零学习OpenCV 4】方框滤波

【从零学习OpenCV 4】高斯滤波

【从零学习OpenCV 4】双边滤波

【从零学习OpenCV 4】边缘检测原理

【从零学习OpenCV 4】Sobel算子 

【从零学习OpenCV 4】Scharr算子

【从零学习OpenCV 4】Laplacian算子

【从零学习OpenCV 4】Canny算法

【从零学习OpenCV 4】图像距离变换

【OpenCV 4开发详解】图像连通域分析

【OpenCV 4开发详解】图像腐蚀

【从零学习OpenCV 4】图像膨胀

【OpenCV 4开发详解】形态学应用

【从零学习OpenCV 4】霍夫变换原理及直线检测

……

经过几个月的努力,市面上第一本OpenCV 4入门书籍《从零学习OpenCV 4》即将由人民邮电出版社发行。如果小伙伴觉得内容有帮助,希望到时候多多支持!
关注小白的小伙伴可以提前看到书中的内容,我们创建了学习交流群,欢迎各位小伙伴添加小白微信加入交流群,添加小白时请备注“学习OpenCV 4”。
16bf58e68b191dcc11b9116ced2b6c81.png
78aa15e9659377504f26697f94f0c6a7.png
Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐