day28:图像分割

图像分割是指将图像中属于某一类的像素点与其他像素点分开, 例如: 在黑白相间的图像中,
将黑色和白色分开就是图像分割.图像分割对于提取图像中的重要信息具有重要的作用.准确的图
像分割有助于提高对图 像内容的理解, 以及后续的图像处理.常见的图像分割算法有漫水填充法、
分水岭法、 Gr abc ut 法、 Mean- Shift 法和 KM eans  法,本 节中 将介绍前4种图像分割方法.
1.漫水填充法:
漫水填充 法是根据像素灰度值之间的差值寻找相同区域以 实现分割.我们可以将图像的灰度值理解成像素点高度, 这样一幅图像可以看成崎岖不平的地面或者山地,向地面上某一个低洼的地方倾倒一定量的水,水将掩盖低于某个高度的区域.漫水填充法利用的就是这样的原理: 其形式与注水相似,因此被形象地称为 漫水"。
与向地面注水一致, 漫水填充法也需要在图像选择一个 注水 像素,该像素称为种子点,种子点按照一定规则不断向外扩散, 从而形成具有相似特征的独立区域, 进而实现图像分割。
漫水填充分三个步骤:
第一步: 选择种子点:(x,y) .
第二步: 以种子 点为中心, 判断4 邻域或者8 邻域的像素值与种子点像素值的差值,将差值
小于阈值的像素点添加进区域内。
第三步: 将新加入的像素点作为新的种子点, 反复执行第二步, 直到没有新的像素点被添加进
该区域为止.
floodFill()函数用于实现漫水填充法分割图像 该函数有两种原型:

 

void visionagin::Myfloodfill()
{
	system("color F0");//dos界面转换成白底
	Mat img = imread("C:\\Users\\86176\\Downloads\\visionimage\\gaoda.jfif");

	int connectivity = 4;//邻域
	RNG rng(10086);
	Mat mask = Mat::zeros(img.rows + 2, img.cols + 2, CV_8UC1);
	Scalar lo = Scalar(20,20,20);
	Scalar up = Scalar(20, 20, 20);
	int flag = connectivity | (255 << 8) | FLOODFILL_FIXED_RANGE;//漫水填充法标志
	while (true)
	{
		//构造漫水中心点
		int py = rng.uniform(0, img.rows - 1);
		int px = rng.uniform(0, img.cols - 1);
		Point point = Point(px, py);
		//彩色图像中填充的颜色
		Scalar newval = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
		int num=floodFill(img, mask, point, newval,0, lo, up, flag);
		//输出漫水填充像素点和数目
		cout << point.x <<" and " << point.y << endl;
		cout << "num is :" << num << endl;
		imshow("分割后填充图像", img);
		imshow("掩码图像", mask);
		int c = waitKey(10);
		if (c==27)
		{
			break;
		}
	}
}

 

2.分水岭法

分水岭法与漫水填充法相似,都是模拟水淹过地面或山地的场景,区别在于漫水填充法是从某
个像素值进行分割, 是一种局部分割算法,而分水岭法是从全局出发,需要对全局进行分割.
分水岭法会在多个局部最低点开始注水,随着注水量的增加,水位越来越高"淹没"局部像
素值较小的像素点,最后两个相邻的凹陷区域的"水"会汇集在一起,并在汇集处形成了"分水岭。
"分水岭"的计算过程是一个迭代标注的过程,经典的计算方式主要分为以下两个步骤.
第一步,排序过程:
        首先对图像像素的灰度级进行排序 确定灰度值较小的像索点,该像素点 即为开始注水点.
第二步,淹没"过程:
        对每个最低点开始不断"注水气不断"淹没"周围的像素点,不同"注水"处的"水"汇集在一起,形成分割线.
通过图像的边缘区域对图像进行标记, 首先利用 canny 函数计算图像的边缘,之后利用 fi ndCon to urs 函数计算图像中的连通域,并通过 d rawContours  函数绘制连通域得到符合格式要求的标记图像 最后利用 watershed  函数对图像进行分割.为了增加分割后不同区域之 间的对比度, 随机对不同区域进行上色.

 

void visionagin:: Mywatershed()
{
	Mat img = imread("C:\\Users\\86176\\Downloads\\visionimage\\std.jfif");
	Mat img_gry, img_canny;
	cvtColor(img, img_gry, COLOR_BGR2GRAY);
	GaussianBlur(img_gry, img_gry, Size(3, 3), 10, 20);
	//提取边缘并进行闭运算
	Canny(img_gry, img_canny, 100, 255);
	Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3));
	morphologyEx(img_canny, img_canny, MORPH_CLOSE, kernel);
	imshow("原图像", img);
	imshow("边缘", img_canny);
	vector<vector<Point>> contours;
	//计算连通域数目
	findContours(img_canny, contours, RETR_TREE, CHAIN_APPROX_SIMPLE);
	//在Mat maskwatershed绘制轮廓,用于存放watershed 的结果
	Mat maskwatershed = Mat::zeros(img_canny.size(), CV_32S);
	for (int i = 0; i < contours.size(); ++i)
	{
		drawContours(maskwatershed, contours, i, Scalar::all(i + 1),-1);
	}
	watershed(img, maskwatershed);
	//随机生成几种颜色
	vector<Vec3b>colors;
	RNG rng(10086);
	for (int j = 0; j < contours.size(); j++)
	{
		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 result = Mat::zeros(img.size(), CV_8UC3);
	for (int row = 0; row < img_canny.rows; row++)
	{
		for (int col = 0; col < img_canny.cols; col++)
		{
			int index = maskwatershed.at<int>(row, col);//必须使用大于0的整数索引
			if (index == -1)//边界
			{
				result.at<Vec3b>(row, col) = Vec3b(255, 255, 255);
			}
			else if (index<=0 || index>contours.size())//没有标记清楚的区域
			{
				result.at<Vec3b>(row, col) = Vec3b(0, 0, 0);
			}
			else
			{
				result.at<Vec3b>(row, col) = colors[index - 1];
			}
		}
	}
	result = result * 0.6 + img * 0.4;
	imshow("分水岭法结果", result);
	//绘制每个区域的图像
	for (int n = 0; n < contours.size(); ++n)
	{
		Mat output = Mat::zeros(img.size(), CV_8UC3);
		for (int row = 0; row < img_canny.rows; row++)
		{
			for (int col = 0; col < img_canny.cols; col++)
			{
				int index = maskwatershed.at<int>(row, col);//必须使用大于0的整数索引
				if (index == n)//边界
				{
					output.at<Vec3b>(row, col) = img.at<Vec3b>(row, col);
				}
				else
				{
					output.at<Vec3b>(row, col) = Vec3b(0, 0, 0);
				}
			}
		}
		imshow(to_string(n), output);
	}

}

 3.Grabcut法

Grabcut 法是重要的图像分割算法,其使用高斯混合模型估计目标区域 背景和前景.该算法
通过迭代的方法解决了能量函数最小化的问题, 使得结果具有更高的可靠性. OpenCV 提供了利
Grabcut 算法分割图像的 grab Cut 函数 .

 

void visionagin::Mygrabcut()
{
	Mat img = imread("C:\\Users\\86176\\Downloads\\visionimage\\std.jfif");
	if (img.empty() == true)
	{
		cout << "读取失败!" << endl;
	}
	Mat imgcopy;
	img.copyTo(imgcopy);
	//绘制矩形
	Rect r =Rect(20, 20, 200, 200);
	rectangle(imgcopy, r, Scalar(0, 0, 200), 1);
	imshow("选择的roi区域", imgcopy);
	//进行分割
	Mat bgmodel = Mat::zeros(1, 65, CV_64FC1);
	Mat fgmodel = Mat::zeros(1, 65, CV_64FC1);
	Mat mask = Mat::zeros(img.size(), CV_8UC1);
	grabCut(img, mask, r, bgmodel, fgmodel, 5, GC_INIT_WITH_RECT);
	//绘制结果
	Mat result;
	for (int i = 0; i < mask.rows; ++i)
	{
		for (int j = 0; j < mask.cols; j++)
		{
			int n = mask.at<uchar>(i, j);
			//将明显为前景和可能为前景的结果保留
			if (n == 1 || n == 3)
			{
				 mask.at<uchar>(i, j)=255;
			}
			//将明显为背景和可能为背景的剔除
			else
			{
				mask.at<uchar>(i, j) = 0;
			}
		}
	}
	bitwise_and(img, img, result, mask);
	imshow("result", result);
}

 4.Mean-Shift法

void visionagin::Mymean_shift()
{
	Mat img = imread("C:\\Users\\86176\\Downloads\\visionimage\\keys1.jfif");
	if (img.empty() == true)
	{
		cout << "读取失败!" << endl;
	}
	resize(img, img, Size(400, 400));
	imshow("原图", img);
	Mat result1, result2;
	TermCriteria T1 = TermCriteria(TermCriteria::COUNT | TermCriteria::EPS, 10, 0.1);
	pyrMeanShiftFiltering(img, result1, 20, 40, 2, T1);//第一次分割
	pyrMeanShiftFiltering(result1, result2, 20, 40, 2, T1);//第二次分割
	//显示分割结果
	imshow("result1", result1);
	imshow("result2", result2);
	Mat imgcanny, res1canny, res2canny;
	Canny(img, imgcanny, 150, 300);
	Canny(result1, res1canny, 150, 300);
	Canny(result2, res2canny, 150, 300);

	imshow("imgcanny", imgcanny);
	imshow("res1canny", res1canny);
	imshow("res2canny", res2canny);
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值