opencv 分水岭算法详细理论+实践

       分水岭算法看了两天了,基本原理看着挺简单,但是opencv中具体的实现方式看着还挺困难的。今天就说说我理解的地方,有很多不太理解的还得以后深入学习时候再补充。

基本原理:分水岭实则为两个盆地的交界处,通过在每个盆地中浸水的方式产生分割边界,两个盆地快要混合到一起的那个边界即为分割边界;

具体实现方式:

通过mark图像(即人工选取初始浸水点)指导浸水过程,通过findContours函数产生contours,然后通过drawContours函数得到mark图像。如下图为一个mark图像。

opencv 分水岭算法详细理论+实践_第1张图片

有3个区域是初始浸水区。opencv分水岭算法过程如下:

初始化mark矩阵,生成最初的注水区域。

1、设置mark图像的边框为-1;

2、标记每个mark区域的边界为-2;

3、对于mark图像的每个像素,如果它本身为0,但上下左右四邻域有一个不为0,则把该点按照RGB值放入相应的队列。队列高度是256,对应(0-255),初始阶段完成后,把-2对应的边界点按照RGB放入相应队列。

opencv 分水岭算法详细理论+实践_第2张图片

之后进入浸水过程,递归描述如下:

for(;;)

{

扫描0-255高度值队列,如果找到一个像素,则弹出该标记,并退出扫描;

如果该像素的四邻域中存在两个不同的非0值,表示该点为注水盆地的边缘,即分水岭,在mark图像中标记该点为-1;

扫描该点的四邻域,是否存在为0的mark域,如果存在的话就把该邻域点标记为RGB值,放入队列;

}

经过递归过程后得到下图:

opencv 分水岭算法详细理论+实践_第3张图片

具体代码如下:

#include
#include
#include
#include
#include
#include
using namespace std;
using namespace cv;

//分水岭图像分割
void watershedSrgment(Mat& src, int& noOfSegment)//noOfSegment表示分割的类别数
{
	Mat grayMat;
	Mat otsuMat;
	cvtColor(src,grayMat,CV_BGR2GRAY);
	threshold(grayMat,otsuMat,0,255,CV_THRESH_BINARY_INV+CV_THRESH_OTSU);
	imshow("src",src);
	imshow("otsuMat",otsuMat);
	//形态学
	morphologyEx(otsuMat,otsuMat,MORPH_OPEN,Mat::ones(9,9,CV_8SC1),Point(4,4),2);//2表示迭代的次数,Point(4,4)表示结构元素的原点
	imshow("Mor-open",otsuMat);
	//距离变换
	Mat disTranMat(otsuMat.rows,otsuMat.cols,CV_32FC1);
	distanceTransform(otsuMat,disTranMat,CV_DIST_L2,3);
	//归一化
	normalize(disTranMat,disTranMat,0.0,1,NORM_MINMAX);
	imshow("DisTranMat",disTranMat);
	//阈值分割
	threshold(disTranMat,disTranMat,0.1,1,CV_THRESH_BINARY);
	normalize(disTranMat,disTranMat,0,255,NORM_MINMAX);
	disTranMat.convertTo(disTranMat,CV_8UC1);
	imshow("TDisTranMat",disTranMat);
	//计算标记的分割块
	int i, j, compCount = 0;
	vector> contours;
	vector hierarchy;
	findContours(disTranMat,contours,hierarchy,CV_RETR_CCOMP,CV_CHAIN_APPROX_SIMPLE);
	Mat markers(disTranMat.size(),CV_32S);
	markers = Scalar::all(0);
	int idx = 0;
	//绘制区域块
	for (; idx >= 0; idx = hierarchy[idx][0], compCount++)
	{
		drawContours(markers,contours,idx,Scalar::all(compCount+1),-1,8,hierarchy,INT_MAX);
	}
	imshow("markers",markers*255);
	double t = (double)getTickCount();
	watershed(src,markers);
	t = (double)getTickCount() - t;
	cout << "time: " << t * 1000 / getTickFrequency() << endl;

	vector colorTab;
	for (int i = 0; i < compCount; i++)
	{
		int b = theRNG().uniform(0,255);
		int g = theRNG().uniform(0,255);
		int r = theRNG().uniform(0,255);
		colorTab.push_back(Vec3b((uchar)b,(uchar)g,(uchar)r));
	}

	Mat watershedImage(markers.size(),CV_8UC3);
	for (int i = 0; i < markers.rows; i++)
	{
		for (int j = 0; j < markers.cols; j++)
		{
			int index = markers.at(i,j);
			if (index == -1)
				watershedImage.at(i, j) = Vec3b((uchar)255, (uchar)255, (uchar)255);
			else if (index <= 0 || index > compCount)
				watershedImage.at(i, j) = Vec3b(0, 0, 0);
			else
				watershedImage.at(i, j) = colorTab[index-1];
		}
	}
	imshow("watershed",watershedImage);
	waitKey(0);
}

int main()
{
	//imread中0表示灰度返回,1表示原图返回
	Mat srcImage = imread("E:\\研究生\\学习材料\\学习书籍\\OpenCV图像处理编程实例-源码-20160801\\《OpenCV图像处理编程实例-源码-20160801\\images\\flower.jpg");
	if (!srcImage.data)
		return -1;
	int noOfSegment = 0;
	watershedSrgment(srcImage, noOfSegment);
	return 0;
}

 

你可能感兴趣的:(opencv学习)