分水 算法——opencv

#include "cv.h"
#include "highgui.h"
#include <stdio.h>
#include <stdlib.h>
using namespace std;
using namespace cv;

IplImage* marker_mask = 0;
IplImage* markers = 0;
IplImage* img0 = 0, *img = 0, *img_gray = 0, *wshed = 0;
CvPoint prev_pt = { -1, -1 };

void on_mouse(int event, int x, int y, int flags, void* param)
{
	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);
		// 实际标记 marker_mask 才会被算法用到
		cvLine(img, prev_pt, pt, cvScalarAll(255), 5, 8, 0);
		// img标记只便于用户观察
		prev_pt = pt;
		cvShowImage("image", img);
	}
}


int main()
{
	printf("Hot keys: \n"
		"\tESC - quit the program\n"
		"\tr - restore the original image\n"
		"\tw or ENTER - 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");

	char* filename = "screen.jpg";
	CvRNG rng = cvRNG(-1);
	// 定义一个随机化生成器并初始化为-1,配合下面cvRandInt(&rng)生成随机数,现在知道为什么会变颜色了吧

	if ((img0 = cvLoadImage(filename, 1)) == 0)
		return 0;

	cvNamedWindow("image", 1);
	cvNamedWindow("watershed transform", 1);

	img = cvCloneImage(img0);
	// 用于显示的原图像
	img_gray = cvCloneImage(img0);
	// 用于和分割出的颜色块进行混合
	wshed = cvCreateImage(cvGetSize(img), 8, 3);
	// 用于存储分割出的颜色块和最后的效果图
	marker_mask = cvCreateImage(cvGetSize(img), 8, 1);
	// 用于记录用户标记区域的画布,并在此基础上制作用于分水岭算法使用的markers
	markers = cvCreateImage(cvGetSize(img), IPL_DEPTH_32S, 1);
	cvCvtColor(img, marker_mask, CV_BGR2GRAY);
	cvCvtColor(marker_mask, img_gray, CV_GRAY2BGR);

	cvZero(marker_mask);
	cvZero(wshed);
	cvShowImage("image", img);
	cvShowImage("watershed transform", wshed);

	cvSetMouseCallback("image", on_mouse, 0);
	// 从这儿开始实现鼠标标记功能,具体可查ICVL

	for (;;)
	{
		char c = cvWaitKey();

		if (c == 27)
			break;

		if (c == 'r')
		{
			cvZero(marker_mask);
			cvCopy(img0, img);
			cvShowImage("image", img);
		}

		if (c == 'w')
		{
			CvMemStorage* storage = cvCreateMemStorage(0);
			CvSeq* contours = 0;

			int comp_count = 1;
			// 粗看以为这是记录轮廓数目呢,其实不然,他将把每个轮廓设为同一像素值
			cvFindContours(marker_mask, storage, &contours, sizeof(CvContour),
				CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE);

			cvZero(markers);
			for (; contours != 0; contours = contours->h_next, comp_count++)
			{
				cvDrawContours(markers, contours, cvScalarAll(comp_count + 1),
					cvScalarAll(comp_count + 1), -1, -1, 8, cvPoint(0, 0));
			}
			// 上面这些最后得到markers 将会是一些数值块,每个轮廓区域内都有同一像素值
			// 到此时Watershed 终于得到了它如饥似渴的 markers 这个markers 中记录了刚刚
			// 用户用鼠标勾勒的感兴趣区域

			CvMat* color_tab;
			color_tab = cvCreateMat(1, comp_count, CV_8UC3);
			// 构造一个一维8bit无符号3通道元素类型的矩阵,用来记录一些随机的颜色
			for (int 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);
			}

			{// 千呼万唤始出来的cvWatershed
				double t = (double)cvGetTickCount();
				cvWatershed(img0, markers);
				t = (double)cvGetTickCount() - t;
				printf("exec time = %gms\n", t / (cvGetTickFrequency()*1000.));
				// 上面的t用来计算此算法运行时间

				/************************************************************************/
				/* markers中包含了一些用户感兴趣的区域,每个区域用1、2、3。。一些像素值标注,经过
				此算法后,markers会变成什么样呢?要知道markers中标注的只是用户用鼠标轻描淡写的
				一些区域,把这些区域想像成一些湖泊,如果只有一个区域,则代表整幅图将会被这一个
				湖泊淹没,上面color_tab 正是用来记录每个湖泊的颜色。如果用户标注了两个区域,则
				湖泊会沿着这两个区域蔓延,直到把图片分成两个湖泊,这两个湖泊不是无规律的,而是
				尽可能把图像的轮廓分隔开。如标注多个区域,则将形成多种颜色的湖泊,此算法会把把
				每个湖泊的分水岭赋为 -1,即用来分隔这些湖泊,下面图片展示了这些湖泊把整幅图都分
				隔开了                                                 */
				/************************************************************************/
			}

			// paint the watershed image
			for (int i = 0; i < markers->height; i++)
			for (int j = 0; j < markers->width; j++)
			{
				int idx = CV_IMAGE_ELEM(markers, int, i, j);
				// idx得到了markers 在(i, j)坐标的的像素值,这个值对应color_tab中的一种颜色
				// 因为markers 中的像素值就是用1-comp_count 的像素值标注的
				uchar* dst = &CV_IMAGE_ELEM(wshed, uchar, i, j * 3);
				// dst得到了wshed图像 (i, j)像素数据的首地址,因为乘3是因为3通道

				if (idx == -1)
					// 在wshed图像中将markers 中得到的分水岭标记为白色,原先-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;
					// 指向idx 所对应的颜色通道,这些颜色是上面随机生成的
					dst[0] = ptr[0]; dst[1] = ptr[1]; dst[2] = ptr[2];
					// 把对应的像素值赋给wshed 图像
				}
			}

			cvAddWeighted(wshed, 0.5, img_gray, 0.5, 0, wshed);
			// 可以注释掉看下效果
			cvShowImage("watershed transform", wshed);
			cvReleaseMemStorage(&storage);
			cvReleaseMat(&color_tab);
		}
	}

	return 1;
}

你可能感兴趣的:(opencv,分水,算法opencv)