1. 程式人生 > >opencv3實現分水嶺演算法-watershed函式

opencv3實現分水嶺演算法-watershed函式

#include<iostream>
#include<opencv2/opencv.hpp>

using namespace std;
using namespace cv;

bool g_bDrawing = false;
Point g_CurrPoint, g_OrgPoint;
int g_nThick = 5, g_nBlue = 255, g_nGreen = 255, g_nRed = 0;
int g_nImageOneValue = 49;
Mat srcImage;
Mat grayImage;
Mat maskImage;

/*注意:不能在毀掉函式中寫入未初始化的矩陣類,所以需要用時,需要寫一個標誌位,然後再在while(1)迴圈內使用*/
void onMouse(int event, int x, int y, int flag, void *param)
{
	Mat &img = *(cv::Mat*)param;

	switch (event)
	{
		//移動滑鼠的時候
	case CV_EVENT_MOUSEMOVE:
	{
							   g_OrgPoint = g_CurrPoint;
							   g_CurrPoint = Point(x, y);

							   if (g_bDrawing == 1)
							   {
								   line(srcImage, g_CurrPoint, g_OrgPoint, Scalar(g_nBlue, g_nGreen, g_nRed), g_nThick);
								   imshow("【滑鼠事件視窗】", srcImage);

								   //在掩碼圖上進行顯示
								   line(maskImage, g_CurrPoint, g_OrgPoint, Scalar(g_nBlue, g_nGreen, g_nRed), g_nThick);
								   imshow("【掩碼影象】", maskImage);
							   }
	}
		break;
		//點選滑鼠左鍵時
	case CV_EVENT_LBUTTONDOWN:
	{
								 g_bDrawing = true;
								 g_OrgPoint = Point(x, y);
								 g_CurrPoint = g_OrgPoint;
	}
		break;
		//鬆開滑鼠左鍵時
	case CV_EVENT_LBUTTONUP:
	{
							   g_bDrawing = false;
	}
		break;
	}
}

int main()
{
	Mat tempImage;
	RNG &rng = theRNG();

	srcImage = imread("2.jpg");

	//用一個變數來儲存原影象
	Mat g_srcImage;
	srcImage.copyTo(g_srcImage);

	//為掩碼圖 分配空間
	//因為後面需要在掩碼影象中尋找輪廓,所以需要一幅單通道的影象
	maskImage.create(srcImage.size(), CV_8UC1);
	maskImage = Scalar::all(0);

	//需要得到一個 三個通道的灰度圖, 以便之後在該灰度圖中畫上彩色,便於看清效果
	//首先將原圖灰度化,再將灰度化的圖轉換到 bgr空間中
	cvtColor(srcImage, tempImage, CV_BGR2GRAY);
	cvtColor(tempImage, grayImage, CV_GRAY2BGR);

	namedWindow("【滑鼠事件視窗】");
	setMouseCallback("【滑鼠事件視窗】", onMouse, 0);

	namedWindow("【滾動條視窗】", 0);
	createTrackbar("thick", "【滾動條視窗】", &g_nThick, 100, 0);
	createTrackbar("Blue", "【滾動條視窗】", &g_nBlue, 255, 0);
	createTrackbar("Green", "【滾動條視窗】", &g_nGreen, 255, 0);
	createTrackbar("Red", "【滾動條視窗】", &g_nRed, 255, 0);
	createTrackbar("Value", "【滾動條視窗】", &g_nImageOneValue, 100, 0);

	char key;
	while (1)
	{
		imshow("【滑鼠事件視窗】", srcImage);
		key = waitKey();
		if (key == 27)
			break;

		//如果檢測到 鍵值是1 則恢復原圖
		if (key == '1')
		{
			g_srcImage.copyTo(srcImage);
			maskImage = Scalar::all(0);
			imshow("【滑鼠事件視窗】", srcImage);
		}

		//如果檢測到空格 則開始執行分水嶺演算法
		if (key == ' ')
		{
			//分水嶺演算法的步驟:
			/*1、首先在掩碼圖中找到輪廓, 目的是讓區域有索引值
			  2、用索引值來替代輪廓所包圍的所有畫素,使在一個輪廓中的畫素,具有相同的畫素值
			  3、利用分水嶺演算法函式將 區域內的畫素值用索引值來代替
			  4、遍歷得到的影象,使具有相同畫素值的元素得到同一種顏色,方便顯示(這一步 是使之前不易顯示的影象得到顯示)*/

			//首先,在掩碼影象中尋找輪廓
			vector<vector<Point>> contours;
			vector<Vec4i> hierarchy;
			findContours(maskImage, contours, hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE);

			//利用分水嶺演算法函式
			//因為該函式的輸出的影象和輸入的影象是一個型別的,所以需要藉助中間變數,同時也要保證中間變數具有索引值
			//這就可以通過在中間變變數中繪製輪廓來實現對輪廓索引值的標註
			Mat midImage(maskImage.size(), CV_32S, Scalar(0));
			for (int i = 0; i < (int)contours.size(); i++)
			{
				//讓每個畫素都得到標記
				drawContours(midImage, contours, i, Scalar(i + 1), -1/*填充*/, 8, hierarchy);
			}
			imshow("【繪製輪廓的圖】", midImage);
			//分水嶺演算法的實現
			//函式中的midImage已經為影象的每個畫素儲存了索引值,就利用這些索引值對原影象進行分水嶺操作
			//以標記作為基準,利用分水嶺演算法進行擴充套件
			watershed(g_srcImage, midImage);

			//顯示分水嶺演算法後的影象
			imshow("【分水嶺演算法結果圖】", midImage);

			//遍歷得到的掩碼影象,用顏色值來替代相同的索引值
			//在這之前需要先定義一個隨機顏色
			vector<Vec3b> rngColor((int)contours.size());
			rngColor.clear();
			for (int i = 0; i < (int)contours.size(); i++)
			{
				uchar blue = rng.uniform(0, 255);
				uchar green = rng.uniform(0, 255);
				uchar red = rng.uniform(0, 255);

				rngColor.push_back(Vec3b(blue, green, red));
			}
			//開始繪製
			Mat waterImage(srcImage.size(), CV_8UC3);
			for (int i = 0; i < midImage.rows; i++)
			{
				for (int j = 0; j < midImage.cols; j++)
				{
					int g_nValue = midImage.at<int>(i, j);

					if (g_nValue == -1)
						waterImage.at<Vec3b>(i, j) = Vec3b(255, 255, 255);
					else if (g_nValue == 0)
						waterImage.at<Vec3b>(i, j) = Vec3b(0, 0, 0);
					else
						waterImage.at<Vec3b>(i, j) = rngColor[g_nValue - 1];
				}
			}
			waterImage = waterImage * ((1 + g_nImageOneValue) / 100.0) + grayImage * (1 - ((1 + g_nImageOneValue) / 100.0));
			imshow("【分水嶺影象】", waterImage);
		}
	}

	return 0;
}