分水岭算法

这篇博客是通过学习看其他博主对于分水岭函数的理解而写,是我学习找资料的一个过程。
自己设置标记点
学习opencv ,图像分割中分水岭算法的感性认识及cvWatershed例子 - fdl19881 - CSDN博客
自动分割

Opencv分水岭算法——watershed自动图像分割用法 - 牧野的博客 - CSDN博客

所谓分水岭算法有好多种实现算法,拓扑学,形态学,浸水模拟和降水模拟等方式。分水岭算法(Watershed Algorithm),是根据分水岭的构成来考虑图像的分割。
分水岭分割方法,是一种基于拓扑理论的数学形态学的分割方法,其基本思想是把图像看作是测地学上的拓扑地貌,图像中每一点像素的灰度值表示该点的海拔高度,每一个局部极小值及其影响区域称为集水盆,而集水盆的边界则形成分水岭。分水岭的概念和形成可以通过模拟浸入过程来说明。在每一个局部极小值表面,刺穿一个小孔,然后把整个模型慢慢浸入水中,随着浸入的加深,每一个局部极小值的影响域慢慢向外扩展,在两个集水盆汇合处构筑大坝,即形成分水岭。
分水岭的计算过程是一个迭代标注过程。分水岭比较经典的计算方法是L. Vincent提出的。在该算法中,分水岭计算分两个步骤,一个是排序过程,一个是淹没过程。首先对每个像素的灰度级进行从低到高排序,然后在从低到高实现淹没过程中,对每一个局部极小值在h阶高度的影响域采用先进先出(FIFO)结构进行判断及标注。
OpenCV学习(7) 分水岭算法(1) - 迈克老狼2012 - 博客园
[OpenCV学习(8) 分水岭算法(2) - 迈克老狼2012 - 博客园]
python OpenCV学习笔记(二十九):图像流域(分水岭)分割算法 - JS_XH的博客 - CSDN博客
opencv 分水岭算法 - Kaido0的博客 - CSDN博客
关于内存有关的知识
CvMemStorage动态内存存储及操作函数 - fulva的专栏 - CSDN博客
关于找轮廓的博客
Opencv笔记——findContours函数 - 殷硕的专栏 - CSDN博客
opencv查找轮廓—cvFindContours && cvDrawCountours 用法及例子 - timidsmile - CSDN博客
产生随机数
学习OpenCV2——产生随机数 - Markala的博客 - CSDN博客
统计代码运行时间
OpenCV getTickCount统计代码运行时间 - pj_sysu的专栏 - CSDN博客
不同的分割算法
图像分割(Image Segmentation) - Wuya - 博客园
下面这个更加全面
Opencv之分水岭原理和实现 - 五仁月饼哭了的博客 - CSDN博客
分水岭代码详解
分水岭算法(Watershed algorithm)与OpenCV实现 - 研究与专注 - CSDN博客
opencv 分水岭算法 - Kaido0的博客 - CSDN博客
关于CV_IMAGE_ELEM 的用法
OpenCV 中CV_IMAGE_ELEM 的使用 - ninanangel的专栏 - CSDN博客
opencv中CV_IMAGE_ELEM的用法读取每个像素 - 体育.委员 - 博客园
关于图像融合的使用
cvAddWeighted的使用 - qq_33485434的博客 - CSDN博客
下面是使用的加上备注代码

#include<cv.h>
#include<highgui.h>
#include<iostream>

using namespace std;

IplImage* marker_mask = 0;//标记掩码图
IplImage* markers = 0;//标记图
IplImage* img0 = 0,//原图
*img = 0,//原图复制图,处理图
*img_gray = 0, //灰度图
*wshed = 0;//分水岭图
CvPoint prev_pt = { -1, -1 };//设为分水岭外围的点(-1,-1),画线开始点

void on_mouse(int event, int x, int y, int flags, void* param)//鼠标响应函数opencv 会自动给函数传入合适的值
{
	if (!img)//没有图片信息直接退出
		return;
	if (event == CV_EVENT_LBUTTONUP || !(flags & CV_EVENT_FLAG_LBUTTON))//如果鼠标事件是左键起来或者是左键拖拽
		prev_pt = cvPoint(-1, -1);
	else if (event == CV_EVENT_LBUTTONDOWN)//如果鼠标事件是左键按下,此时开始画线
		prev_pt = cvPoint(x, y);
	else if (event == CV_EVENT_MOUSEMOVE && (flags & CV_EVENT_FLAG_LBUTTON))//如果鼠标事件是鼠标移动而且鼠标拖动
	{
		CvPoint pt = cvPoint(x, y);//鼠标现在的位置,画线终止点
		if (prev_pt.x < 0)
			prev_pt = pt;
		cvLine(marker_mask, prev_pt, pt, cvScalarAll(255), 5, 8, 0);//CvScalar 成员:double val[4] RGBA值A=alpha
		cvLine(img, prev_pt, pt, cvScalarAll(255), 5, 8, 0);//画出白色的线
		prev_pt = pt;
		cvShowImage("image", img);
	}
}

int main(int argc, char** argv)
{
	char* filename = (char*)"lena.jpg";//得到图像
	CvMemStorage* storage = cvCreateMemStorage(0);//创建内存,用来放检测到的轮廓
	CvRNG rng = cvRNG(-1);
	if ((img0 = cvLoadImage(filename, 1)) == 0)//没有得到图片就退出
		return 0;
	printf("Hot keys: \n"
		"\tESC - quit the program\n"
		"\tr - restore the original image\n"
		"\tw or SPACE - run watershed algorithm\n"
		"\t\t(before running it, roughly mark the areas on the image)\n"
		"\t  (before that, roughly outline several markers on the image)\n");
	cvNamedWindow("image", 1);
	cvNamedWindow("watershed transform", 1);

	//利用函数复制图像像素点
	img = cvCloneImage(img0);
	img_gray = cvCloneImage(img0);
	wshed = cvCloneImage(img0);
	//利用函数得到图像大小
	marker_mask = cvCreateImage(cvGetSize(img), 8, 1);
	markers = cvCreateImage(cvGetSize(img), IPL_DEPTH_32S, 1);
	//灰度化
	cvCvtColor(img, marker_mask, CV_BGR2GRAY);
	cvCvtColor(marker_mask, img_gray, CV_GRAY2BGR);//这两句只用将RGB转成3通道的灰度图即R=G=B,用来显示用
	//初始化图像
	cvZero(marker_mask);
	cvZero(wshed);

	cvShowImage("image", img);
	cvShowImage("watershed transform", wshed);
	//调用鼠标响应事件
	cvSetMouseCallback("image", on_mouse, 0);
	for (;;)
	{
		int c = cvWaitKey(0);
		if ((char)c == 27)
			break;
		if ((char)c == 'r')
		{
			cvZero(marker_mask);//重新制作标记图
			cvCopy(img0, img);//cvCopy()也可以这样用,不影响原img0图像,也随时更新
			cvShowImage("image", img);
		}
		if ((char)c == 'w' || (char)c == ' ')
		{
			CvSeq* contours = 0;//是一个结构体,输出参数:包含第一个输出轮廓的指针 
			CvMat* color_tab = 0;//标记颜色
			int i, j,
				comp_count = 0;//得到分水岭轮廓数目个数

			//下面选将标记的图像取得其轮廓, 将每种轮廓用不同的整数表示
			//不同的整数使用分水岭算法时,就成为不同的种子点
			//算法本来就是以各个不同的种子点为中心扩张

			cvClearMemStorage(storage);//释放内存

			cvFindContours(marker_mask, storage, &contours, sizeof(CvContour),
				CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE);//得到轮廓点图
			//image=marker_mask				:输入的 8-比特、单通道图像. 非零元素被当成 1, 0 象素值保留为 0 - 从而图像被看成二值的。
			//storage=storage				:得到的轮廓的存储容器 
			//first_contour=&contours		:输出参数:包含第一个输出轮廓的指针
			//header_size=sizeof(CvContour)	:如果 method=CV_CHAIN_CODE,则序列头的大小 >=sizeof(CvChain),否则 >=sizeof(CvContour) . 
			//mode=CV_RETR_CCOMP			:提取模式. ,检测所有轮廓*/, 
			//method=CV_CHAIN_APPROX_SIMPLE	:逼近方法 (对所有节点, 不包括使用内部逼近的 CV_RETR_RUNS). ,只是存储水平,垂直,对角直线的起始点
			//offset每一个轮廓点的偏移量.当轮廓是从图像 ROI 中提取出来的时候,使用偏移量有用,因为可以从整个图像上下文来对轮廓做分析.
			cvZero(markers);
			for (; contours != 0; contours = contours->h_next, comp_count++) //对marks进行标记,对不同区域的轮廓进行编号,相当于设置注水点,有多少轮廓,就有多少注水点  
			{
				cvDrawContours(markers, contours, cvScalarAll(comp_count + 1),
					cvScalarAll(comp_count + 1), -1, -1, 8, cvPoint(0, 0));
			}
			//cvShowImage("image",markers);
			if (comp_count == 0)
				continue;
			color_tab = cvCreateMat(1, comp_count, CV_8UC3);//创建随机颜色列表
			for (i = 0; i < comp_count; i++)//不同的整数标记
			{
				uchar* ptr = color_tab->data.ptr + i * 3;
				ptr[0] = (uchar)(cvRandInt(&rng) % 180 + 50);
				ptr[1] = (uchar)(cvRandInt(&rng) % 180 + 50);
				ptr[2] = (uchar)(cvRandInt(&rng) % 180 + 50);
			}
			double t = (double)cvGetTickCount();//统计代码运行时间
			cvWatershed(img0, markers);//得到最后的分水岭结果图
			cvSave("img0.xml", markers);
			t = (double)cvGetTickCount() - t;
			printf("exec time = %gms\n", t / (cvGetTickFrequency()*1000.));
			// paint the watershed image画出分水岭图
			for (i = 0; i < markers->height; i++)
				for (j = 0; j < markers->width; j++)
				{
				int idx = CV_IMAGE_ELEM(markers, int, i, j);//markers的数据类型为IPL_DEPTH_32S
				uchar* dst = &CV_IMAGE_ELEM(wshed, uchar, i, j * 3);//BGR三个通道的数是一起的,故要j*3
				if (idx == -1) //输出时若为-1,表示各个部分的边界
					dst[0] = dst[1] = dst[2] = (uchar)255;
				else if (idx <= 0 || idx > comp_count) //异常情况
					dst[0] = dst[1] = dst[2] = (uchar)0; // should not get here
				else //正常情况
				{
					uchar* ptr = color_tab->data.ptr + (idx - 1) * 3;
					dst[0] = ptr[0]; dst[1] = ptr[1]; dst[2] = ptr[2];
				}
				}
			cvAddWeighted(wshed, 0.5, img_gray, 0.5, 0, wshed);//wshed.x.y=0.5*wshed.x.y+0.5*img_gray+0加权融合图像
			cvShowImage("watershed transform", wshed);
			cvReleaseMat(&color_tab);
		}
	}
	return 1;
}

在这里插入图片描述

大神自己编写的分水岭代码

分水岭算法的详细介绍(c代码) 柠檬_新浪博客

猜你喜欢

转载自blog.csdn.net/qq_40584593/article/details/85119022