1. 程式人生 > >5.5用分水嶺演算法實現影象分割

5.5用分水嶺演算法實現影象分割

<img src="https://img-blog.csdn.net/20160409220852681?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="" />直接上程式碼吧,還不是太理解
//watershedSegmentation.h
#if !defined WATERSHS
#define WATERSHS

#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>

class WatershedSegmenter {<img src="https://img-blog.csdn.net/20160409220658944?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="" /><img src="https://img-blog.csdn.net/20160409220658944?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="" />

private:

	cv::Mat markers;

public:

	void setMarkers(const cv::Mat& markerImage) {

		// Convert to image of ints
		markerImage.convertTo(markers,CV_32S);
	}

	cv::Mat process(const cv::Mat &image) {

		// Apply watershed
		cv::watershed(image,markers);

		return markers;
	}

	// Return result in the form of an image
	cv::Mat getSegmentation() {

		cv::Mat tmp;
		// all segment with label higher than 255
		// will be assigned value 255
		markers.convertTo(tmp,CV_8U);

		return tmp;
	}

	// Return watershed in the form of an image
	cv::Mat getWatersheds() {

		cv::Mat tmp;
		markers.convertTo(tmp,CV_8U,255,255);

		return tmp;
	}
};


#endif//
//main
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
#include "watershedSegmentation.h"


int main()
{
#if 1


	// Read input image
	cv::Mat image= cv::imread("C:\\Users\\Administrator\\Desktop\\工作\\testp\\group.jpg");
	if (!image.data)
		return 0; 

	// Display the image
	cv::namedWindow("Original Image");
	cv::imshow("Original Image",image);

	// Get the binary map
	cv::Mat binary;
	binary= cv::imread("C:\\Users\\Administrator\\Desktop\\工作\\testp\\binary.bmp",0);

	// Display the binary image
	cv::namedWindow("Binary Image");
	cv::imshow("Binary Image",binary);

	// Eliminate noise and smaller objects
	cv::Mat fg;
	cv::erode(binary,fg,cv::Mat(),cv::Point(-1,-1),6);

	// Display the foreground image
	cv::namedWindow("Foreground Image");
	cv::imshow("Foreground Image",fg);
	cv::imwrite("ForegroundImage.jpg",fg);
	// Identify image pixels without objects
	cv::Mat bg;
	cv::dilate(binary,bg,cv::Mat(),cv::Point(-1,-1),6);
	cv::threshold(bg,bg,1,128,cv::THRESH_BINARY_INV);

	// Display the background image
	cv::namedWindow("Background Image");
	cv::imshow("Background Image",bg);
	//cv::imwrite("BackgroundImage.jpg",bg);
	// Show markers image
	cv::Mat markers(binary.size(),CV_8U,cv::Scalar(0));
	markers= fg+bg;
	cv::namedWindow("Markers");
	cv::imshow("Markers",markers);
	//cv::imwrite("Markers.jpg",markers);
	// Create watershed segmentation object
	WatershedSegmenter segmenter;

	// Set markers and process
	segmenter.setMarkers(markers);
	segmenter.process(image);
//	imshow("11",markers);
	// Display segmentation result
	cv::namedWindow("Segmentation");
	cv::imshow("Segmentation",segmenter.getSegmentation());
	//cv::imwrite("Segmentation.jpg",segmenter.getSegmentation());
	// Display watersheds
	cv::namedWindow("Watersheds");
	cv::imshow("Watersheds",segmenter.getWatersheds());
//	cv::imwrite("Watersheds.jpg",segmenter.getWatersheds());	
#endif	



#if 1
	// Open another image
	 image= cv::imread("C:\\Users\\Administrator\\Desktop\\工作\\testp\\tower.jpg");

	// Identify background pixels
	cv::Mat imageMask(image.size(),CV_8U,cv::Scalar(0));
	cv::rectangle(imageMask,cv::Point(5,5),cv::Point(image.cols-5,image.rows-5),cv::Scalar(255),3);
	// Identify foreground pixels (in the middle of the image)
	cv::rectangle(imageMask,cv::Point(image.cols/2-10,image.rows/2-10),
		cv::Point(image.cols/2+10,image.rows/2+10),cv::Scalar(1),10);

	// Set markers and process
	segmenter.setMarkers(imageMask);
//	imshow("mask+rectangle",imageMask);
	segmenter.process(image);
	//imshow("seg_prosswater",segmenter.process(image));
	// Display the image with markers
	cv::rectangle(image,cv::Point(5,5),cv::Point(image.cols-5,image.rows-5),cv::Scalar(255,255,255),3);
	cv::rectangle(image,cv::Point(image.cols/2-10,image.rows/2-10),
		cv::Point(image.cols/2+10,image.rows/2+10),cv::Scalar(1,1,1),10);
	cv::namedWindow("Image with marker");
	cv::imshow("Image with marker",image);
	//cv::imwrite("Image with marker.jpg",image);
	// Display watersheds
	cv::namedWindow("Watersheds of foreground object");
	cv::imshow("Watersheds of foreground object",segmenter.getWatersheds());
	//cv::imwrite("Watersheds of foreground object.jpg",segmenter.getWatersheds());
#endif
	cv::waitKey();
	return 0;
} <pre code_snippet_id="1640334" snippet_file_name="blog_20160409_7_2417268" name="code" class="cpp"></pre><pre code_snippet_id="1640334" snippet_file_name="blog_20160409_8_847105" name="code" class="cpp">不是太理解,標記影象第一副影象是前景和背景,但是後面一副影象直接是2個矩形。。反正經過處理之後影象的邊緣畫素值為-1

下面是opencv3的程式碼 來自毛星雲

//--------------------------------------【程式說明】-------------------------------------------
//		程式說明:《OpenCV3程式設計入門》OpenCV2版書本配套示例程式77
//		程式描述:分水嶺演算法綜合示例
//		開發測試所用作業系統: Windows 7 64bit
//		開發測試所用IDE版本:Visual Studio 2010
//		開發測試所用OpenCV版本:	2.4.9
//		2014年06月 Created by @淺墨_毛星雲
//		2014年11月 Revised by @淺墨_毛星雲
//------------------------------------------------------------------------------------------------



//---------------------------------【標頭檔案、名稱空間包含部分】----------------------------
//		描述:包含程式所使用的標頭檔案和名稱空間
//------------------------------------------------------------------------------------------------
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <iostream>
using namespace cv;
using namespace std;

//-----------------------------------【巨集定義部分】-------------------------------------------- 
//  描述:定義一些輔助巨集 
//------------------------------------------------------------------------------------------------ 
#define WINDOW_NAME1 "【程式視窗1】"        //為視窗標題定義的巨集 
#define WINDOW_NAME2 "【分水嶺演算法效果圖】"        //為視窗標題定義的巨集

//-----------------------------------【全域性函變數宣告部分】--------------------------------------
//		描述:全域性變數的宣告
//-----------------------------------------------------------------------------------------------
Mat g_maskImage, g_srcImage;
Point prevPt(-1, -1);

//-----------------------------------【全域性函式宣告部分】--------------------------------------
//		描述:全域性函式的宣告
//-----------------------------------------------------------------------------------------------
static void ShowHelpText();
static void on_Mouse( int event, int x, int y, int flags, void* );


//-----------------------------------【main( )函式】--------------------------------------------
//		描述:控制檯應用程式的入口函式,我們的程式從這裡開始執行
//-----------------------------------------------------------------------------------------------
int main( int argc, char** argv )
{	
	//【0】改變console字型顏色
	system("color 6F"); 

	//【0】顯示幫助文字
	ShowHelpText( );

	//【1】載入原圖並顯示,初始化掩膜和灰度圖
	g_srcImage = imread("1.jpg", 1);
	imshow( WINDOW_NAME1, g_srcImage );
	Mat srcImage,grayImage;
	g_srcImage.copyTo(srcImage);
	cvtColor(g_srcImage, g_maskImage, COLOR_BGR2GRAY);
	cvtColor(g_maskImage, grayImage, COLOR_GRAY2BGR);
	g_maskImage = Scalar::all(0);

	//【2】設定滑鼠回撥函式
	setMouseCallback( WINDOW_NAME1, on_Mouse, 0 );

	//【3】輪詢按鍵,進行處理
	while(1)
	{
		//獲取鍵值
		int c = waitKey(0);

		//若按鍵鍵值為ESC時,退出
		if( (char)c == 27 )
			break;

		//按鍵鍵值為2時,恢復源圖
		if( (char)c == '2' )
		{
			g_maskImage = Scalar::all(0);
			srcImage.copyTo(g_srcImage);
			imshow( "image", g_srcImage );
		}

		//若檢測到按鍵值為1或者空格,則進行處理
		if( (char)c == '1' || (char)c == ' ' )
		{
			//定義一些引數
			int i, j, compCount = 0;
			vector<vector<Point> > contours;
			vector<Vec4i> hierarchy;

			//尋找輪廓
			findContours(g_maskImage, contours, hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE);

			//輪廓為空時的處理
			if( contours.empty() )
				continue;

			//拷貝掩膜
			Mat maskImage(g_maskImage.size(), CV_32S);
			maskImage = Scalar::all(0);

			//迴圈繪製出輪廓    //感覺這裡繪製輪廓不重要,重要的是幾個 因為後面又覆蓋了maskImage watershed( srcImage, maskImage );
			for( int index = 0; index >= 0; index = hierarchy[index][0], compCount++ )
				drawContours(maskImage, contours, index, Scalar::all(compCount+1), -1, 8, hierarchy, INT_MAX);

			//compCount為零時的處理
			if( compCount == 0 )
				continue;

			//生成隨機顏色
			vector<Vec3b> colorTab;
			for( 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));
			}

			//計算處理時間並輸出到視窗中
			double dTime = (double)getTickCount();
			watershed( srcImage, maskImage );
			dTime = (double)getTickCount() - dTime;
			printf( "\t處理時間 = %gms\n", dTime*1000./getTickFrequency() );

			//雙層迴圈,將分水嶺影象遍歷存入watershedImage中
			Mat watershedImage(maskImage.size(), CV_8UC3);
			for( i = 0; i < maskImage.rows; i++ )
				for( j = 0; j < maskImage.cols; j++ )
				{
					int index = maskImage.at<int>(i,j);           //不同的index用不同的index顏色
					if( index == -1 )
						watershedImage.at<Vec3b>(i,j) = Vec3b(255,255,255);
					else if( index <= 0 || index > compCount )
						watershedImage.at<Vec3b>(i,j) = Vec3b(0,0,0);
					else
						watershedImage.at<Vec3b>(i,j) = colorTab[index - 1];
				}

				//混合灰度圖和分水嶺效果圖並顯示最終的視窗
				watershedImage = watershedImage*0.5 + grayImage*0.5;
				imshow( WINDOW_NAME2, watershedImage );
		}
	}

	return 0;
}


//-----------------------------------【onMouse( )函式】---------------------------------------
//		描述:滑鼠訊息回撥函式
//-----------------------------------------------------------------------------------------------
static void on_Mouse( int event, int x, int y, int flags, void* )
{
	//處理滑鼠不在視窗中的情況
	if( x < 0 || x >= g_srcImage.cols || y < 0 || y >= g_srcImage.rows )
		return;

	//處理滑鼠左鍵相關訊息
	if( event == CV_EVENT_LBUTTONUP || !(flags & CV_EVENT_FLAG_LBUTTON) )
		prevPt = Point(-1,-1);
	else if( event == CV_EVENT_LBUTTONDOWN )
		prevPt = Point(x,y);

	//滑鼠左鍵按下並移動,繪製出白色線條
	else if( event == CV_EVENT_MOUSEMOVE && (flags & CV_EVENT_FLAG_LBUTTON) )
	{
		Point pt(x, y);
		if( prevPt.x < 0 )
			prevPt = pt;
		line( g_maskImage, prevPt, pt, Scalar::all(255), 5, 8, 0 );
		line( g_srcImage, prevPt, pt, Scalar::all(0), 5, 8, 0 );
		prevPt = pt;
		imshow(WINDOW_NAME1, g_srcImage);
	}
}


//-----------------------------------【ShowHelpText( )函式】----------------------------------  
//      描述:輸出一些幫助資訊  
//----------------------------------------------------------------------------------------------  
static void ShowHelpText()  
{  
	//輸出歡迎資訊和OpenCV版本
	printf("\n\n\t\t\t非常感謝購買《OpenCV3程式設計入門》一書!\n");
	printf("\n\n\t\t\t此為本書OpenCV2版的第77個配套示例程式\n");
	printf("\n\n\t\t\t   當前使用的OpenCV版本為:" CV_VERSION );
	printf("\n\n  ----------------------------------------------------------------------------\n");

	//輸出一些幫助資訊  
	printf(  "\n\n\n\t歡迎來到【分水嶺演算法】示例程式~\n\n");  
	printf(  "\t請先用滑鼠在圖片視窗中標記出大致的區域,\n\n\t然後再按鍵【1】或者【SPACE】啟動演算法。"
		"\n\n\t按鍵操作說明: \n\n"  
		"\t\t鍵盤按鍵【1】或者【SPACE】- 執行的分水嶺分割演算法\n"  
		"\t\t鍵盤按鍵【2】- 恢復原始圖片\n"  
		"\t\t鍵盤按鍵【ESC】- 退出程式\n\n\n");  
}