前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >修正!【从零学习OpenCV 4】分割图像——分水岭法

修正!【从零学习OpenCV 4】分割图像——分水岭法

作者头像
小白学视觉
发布2020-02-27 13:22:54
1.3K0
发布2020-02-27 13:22:54
举报

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

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

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

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

代码语言:javascript
复制
代码清单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给出。

代码语言:javascript
复制
代码清单8-20 myWatershed.cpp分水岭法分割图像
#include <opencv2\opencv.hpp>
#include <iostream>

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<vector<Point>> contours;
	vector<Vec4i> 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<Vec3b> 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<int>(i, j);
			if (index == -1)  // 区域间的值被置为-1(边界)
			{
				resultImg.at<Vec3b>(i, j) = Vec3b(255, 255, 255);
			}
			else if (index <= 0 || index > contours.size())  // 没有标记清楚的区域被置为0 
			{
				resultImg.at<Vec3b>(i, j) = Vec3b(0, 0, 0);
			}
			else  // 其他每个区域的值保持不变:1,2,…,contours.size()
			{
				resultImg.at<Vec3b>(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<int>(i, j);
				if (index == n)
					resImage1.at<Vec3b>(i, j) = img.at<Vec3b>(i, j);
				else
					resImage1.at<Vec3b>(i, j) = Vec3b(0, 0, 0);
			}
		}
		//显示图像
		imshow(to_string(n), resImage1);
	}

	waitKey(0);
	return 0;
}

图8-12 myWatershed.cpp程序中分水岭分割结果

图8-13 myWatershed.cpp程序中被分割区域的原图像

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-02-26,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 小白学视觉 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档