opencv入门(18)-- 基于距离变换的分水岭分割

18.1 概述

        图像分割(Image Segmentation)是对图像中指定对象进行定位轮廓分割。图像分割的目的是将图像中像素根据一定规则分为若干个簇(cluster)集合,每个集合包办一类像素。根据算法可分为监督学习和无监督学习。

        距离变换:可以参考点与多边形的关系,可以算出每个像素点到多边形轮廓的距离得到距离图像。距离变换常见的算法有:不断膨胀/腐蚀得到和基于倒角距离两种。定义是计算一个图像中非零像素点到最近的零像素点的距离,也就是到零像素点的最短距离。距离变换将二值图像变换为灰度图像(其中每个像素的灰度值等于他到最近目标点的距离)。

        分水岭变换:可以将像素灰度值比拟为山地拓扑图,灰度值的大小相当于海拔高度。常见算法是基于浸泡理论实现的。

18.2 相关API

·void distanceTransform( InputArray src, OutputArray det, OutputArray labels, int distanceType, int maskSize, int labelType=DIST_LABEL_CCOMP)

参数含义
作用距离变换,用于计算图像中每一个非零点像素与其最近的零点像素之间的距离,输出的是保存每一个非零点与最近零点的距离信息
输入src

多边形轮廓

dst输出距离图像,8位整型会32位浮点型单通道
labels输出的二维标签(离散维诺图),类型CV_32SC1
distanceType

距离类型:

maskSize

距离掩膜的大小3或5,推荐用3

labelType

输出二维数组的类型

返回值void

·void watershed( InputArray image, InputOutputArray markers)

参数含义
作用分水岭算法
输入image

原图

markers分水岭的种子信息,包括前景,背景,和未知区域
返回值void

18.3 分水岭处理流程

18.3.1 图像二值化(得到准确的目标位置)

不同的图像获取二值图的方式可能需要根据图像的实际情况进行图像的预处理,已得到目标物的区域二值图。

	vector<Mat> sigleImgs;
	split(src, sigleImgs);
	Mat bImg = sigleImgs[0];
	Mat gImg = sigleImgs[1];
	Mat rImg = sigleImgs[2];

	//二值化,分两段进行阈值处理
	Mat binaryImg1, binaryImg2, binaryImg3;
	Mat grayImg;
	cvtColor(src, grayImg,COLOR_BGR2GRAY);
	imshow("grayImg", grayImg);
	threshold(rImg, binaryImg1,190,255,THRESH_BINARY);
    //金色硬币的灰度值在整体灰度值中间,进行分段处理
	threshold(grayImg, binaryImg2, 137, 255, THRESH_TOZERO_INV);
	threshold(binaryImg2, binaryImg3, 100, 255, THRESH_BINARY);

    //利用闭、开操作实现填充和非目标物的去除
	Mat closeImg1, openImg1;;
	Mat closeKernel1 = getStructuringElement(MORPH_RECT, Size(5, 5));
	Mat openKernel1 = getStructuringElement(MORPH_RECT, Size(19, 19));
	morphologyEx(binaryImg1, closeImg1, MORPH_CLOSE, closeKernel1);
	morphologyEx(closeImg1, openImg1, MORPH_OPEN, openKernel1);
    
	Mat closeImg, openImg;
	Mat closeKernel = getStructuringElement(MORPH_RECT,Size(7,7));
	Mat openKernel = getStructuringElement(MORPH_RECT, Size(29, 29));
	morphologyEx(binaryImg3, closeImg, MORPH_CLOSE,closeKernel);
	morphologyEx(closeImg, openImg, MORPH_OPEN,openKernel);

    //合并图像
	Mat dstBinary;
	bitwise_or(openImg1, openImg, dstBinary);
	imshow("dstBinary", dstBinary);

18.3.2 找到背景

通过形态学膨胀,放大目标区域,确定背景位置;

	//膨胀,使边缘区域变大
	Mat backImg=Mat(dstBinary.size(),CV_8UC1);
	Mat dilateKernel = getStructuringElement(MORPH_RECT, Size(5, 5));
	dilate(dstBinary, backImg, dilateKernel);
	//imshow("backImg", backImg);

18.3.3 距离变化

	//距离变换
	Mat distImg;
	distanceTransform(dstBinary, distImg,DIST_L2,3);
	//归一化
	normalize(distImg, distImg,0,1,NORM_MINMAX);
	imshow("distImg", distImg)

18.3.4 找到前景盆底

在距离变换的基础上找到每个目标的盆地中心,得到前景

	//再次二值化,前景盆地
	threshold(distImg, distImg,0.3*1,1,THRESH_BINARY);
	normalize(distImg, distImg, 0, 255, NORM_MINMAX);
	Mat k1 = Mat::ones(3, 3, CV_8UC1);
	erode(distImg, distImg,k1,Point(-1,-1));
	imshow("distImg00", distImg);

18.3.5 获取边缘可能位置

    //求边缘的可能区域
	Mat dist8u;
	distImg.convertTo(dist8u, CV_8U);
    //前景背景相减	
	Mat unknowPos;
	subtract(backImg, dist8u, unknowPos);
	imshow("unknowPos", unknowPos);

18.3.5 创建markers

	//计算连通域,构造marker
	Mat markersTemp;
	int num = connectedComponents(dist8u, markersTemp);
	markersTemp = markersTemp + 1;
	for (int i = 0; i < dist8u.rows; i++)
	{
		for (int j = 0; j < dist8u.cols; j++)
		{
			if (unknowPos.at<uchar>(i, j) == 255)
			{
				markersTemp.at<int>(i, j) = 0;
			}
			else;
		}
	}

18.3.6 分水岭分割

	//分水岭算法
	watershed(src, markersTemp);

18.3.7 区域标注

	// 随机生成num-1种颜色,1背景
	vector<Vec3b> colors;  
	for (int i = 1; i < num; i++)
	{
	    int b = rng.uniform(0, 255);
		int g = rng.uniform(0, 255);
		int r = rng.uniform(0, 255);
		colors.push_back(Vec3b((uchar)b, (uchar)g, (uchar)r));
	}

	Mat resultImg = Mat(src.size(), CV_8UC3);  //显示图像
	//src.copyTo(resultImg);
	for (int i = 0; i < resultImg.rows; i++)
	{
		for (int j = 0; j < resultImg.cols; j++)
		{
			// 绘制每个区域的颜色
			int index = markersTemp.at<int>(i, j);
			if (index == -1)  // 区域间的值被置为-1(边界)
		    { 
				resultImg.at<Vec3b>(i, j) = Vec3b(0, 0, 255);
			}
			else if (index > 1&& index <9)
			{
				resultImg.at<Vec3b>(i, j) = colors[index - 2];
			}
			else;
		}
	}
	resultImg = resultImg*0.6 + src*0.4;
	imshow("resultImg", resultImg);

 

18.4 完整代码示例

int main(int argc, char** argv)
{
    RNG rng(12345);
	//源图像
	src = imread("D:\\testimg\\CSet12\\yingbi.png");
	if (src.empty())
	{
		printf("Could not load the image...\n");
		return -1;
	}
	else;

	namedWindow("inputImg", WINDOW_AUTOSIZE);
	imshow("inputImg", src);
	namedWindow(OUTPUTWIN, WINDOW_AUTOSIZE);

	vector<Mat> sigleImgs;
	split(src, sigleImgs);

	Mat bImg = sigleImgs[0];
	Mat gImg = sigleImgs[1];
	Mat rImg = sigleImgs[2];

	//二值化
	Mat binaryImg1, binaryImg2, binaryImg3;
	Mat grayImg;
	cvtColor(src, grayImg,COLOR_BGR2GRAY);
	imshow("grayImg", grayImg);
	threshold(rImg, binaryImg1,190,255,THRESH_BINARY);
	threshold(grayImg, binaryImg2, 137, 255, THRESH_TOZERO_INV);
	threshold(binaryImg2, binaryImg3, 100, 255, THRESH_BINARY);

	Mat closeImg1, openImg1;;
	Mat closeKernel1 = getStructuringElement(MORPH_RECT, Size(5, 5));
	Mat openKernel1 = getStructuringElement(MORPH_RECT, Size(19, 19));
	morphologyEx(binaryImg1, closeImg1, MORPH_CLOSE, closeKernel1);
	morphologyEx(closeImg1, openImg1, MORPH_OPEN, openKernel1);
	//imshow("closeImg1", closeImg1);
	imshow("openImg1", openImg1);

	Mat closeImg, openImg;
	Mat closeKernel = getStructuringElement(MORPH_RECT,Size(7,7));
	Mat openKernel = getStructuringElement(MORPH_RECT, Size(29, 29));
	morphologyEx(binaryImg3, closeImg, MORPH_CLOSE,closeKernel);
	morphologyEx(closeImg, openImg, MORPH_OPEN,openKernel);
	//imshow("closeImg", closeImg);
	imshow("openImg", openImg);

	Mat dstBinary;
	bitwise_or(openImg1, openImg, dstBinary);
	imshow("dstBinary", dstBinary);

	//膨胀,使边缘区域变大
	Mat backImg=Mat(dstBinary.size(),CV_8UC1);
	Mat dilateKernel = getStructuringElement(MORPH_RECT, Size(5, 5));
	dilate(dstBinary, backImg, dilateKernel);
	imshow("backImg", backImg);

	//距离变换
	Mat distImg;
	distanceTransform(dstBinary, distImg,DIST_L2,3);
	//归一化
	normalize(distImg, distImg,0,1,NORM_MINMAX);
	imshow("distImg", distImg);

	//再次二值化,前景盆地
	threshold(distImg, distImg,0.3*1,1,THRESH_BINARY);
	normalize(distImg, distImg, 0, 255, NORM_MINMAX);
	Mat k1 = Mat::ones(3, 3, CV_8UC1);
	erode(distImg, distImg,k1,Point(-1,-1));
	imshow("distImg00", distImg);

	//marker标记
	Mat dist8u;
	distImg.convertTo(dist8u, CV_8U);


	//求边缘的可能区域
	Mat unknowPos;
	subtract(backImg, dist8u, unknowPos);
	imshow("unknowPos", unknowPos);

	//计算连通域,构造marker
	Mat markersTemp;
	int num = connectedComponents(dist8u, markersTemp);
	markersTemp = markersTemp + 1;
	for (int i = 0; i < dist8u.rows; i++)
	{
		for (int j = 0; j < dist8u.cols; j++)
		{
			if (unknowPos.at<uchar>(i, j) == 255)
			{
				markersTemp.at<int>(i, j) = 0;
			}
			else;
		}
	}

	//分水岭算法
	watershed(src, markersTemp);

	// 随机生成num-1种颜色,1背景
	vector<Vec3b> colors;  
	for (int i = 1; i < num; i++)
	{
	    int b = rng.uniform(0, 255);
		int g = rng.uniform(0, 255);
		int r = rng.uniform(0, 255);
		colors.push_back(Vec3b((uchar)b, (uchar)g, (uchar)r));
	}

	Mat resultImg = Mat(src.size(), CV_8UC3);  //显示图像
	//src.copyTo(resultImg);
	for (int i = 0; i < resultImg.rows; i++)
	{
		for (int j = 0; j < resultImg.cols; j++)
		{
			// 绘制每个区域的颜色
			int index = markersTemp.at<int>(i, j);
			if (index == -1)  // 区域间的值被置为-1(边界)
		    { 
				resultImg.at<Vec3b>(i, j) = Vec3b(0, 0, 255);
			}
			else if (index > 1&& index <9)
			{
				resultImg.at<Vec3b>(i, j) = colors[index - 2];
			}
			else;
		}
	}
	resultImg = resultImg*0.6 + src*0.4;
	imshow("resultImg", resultImg);

	waitKey(0);
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值