基於GrabCut的自動圖像分割

 

上一篇文章中介紹了GrabCut的交互性分割,在這篇文章中,實現了對圖像的自動分割。

首先,在GrabCut的交互分割中,我們通過用戶自己畫框,來框出包含目標的區域。所以,只要我們能夠確定這個區域,再使用GrabCut算法就可以分割了。

實現思路

  1. Otsu算法選擇最佳閾值對圖像進行二值化,
  2. 再通過數學形態學處理(可以使得膨脹後的圖像比目標大)
  3. 然後提取最大連通區域(這就獲得了我們要的區域)
  4. 用GrabCut算法分割

代碼

#include "WatershedSegment.h"
#include <string>
#include <iostream>

using namespace std;
using namespace cv;
Mat g_image1;  //原圖
Mat g_binary;	   //二值化圖像
Mat g_fg1;//前景像素
Mat g_bg1;//背景像素
Mat g_markers1;//合成的標記圖像
Rect g_maxRect;//目標的最大連通區域
/*進行GrabCut需要的參數*/
Mat bgModel, fgModel, mask;
/*讀取原圖*/
void ReadOriginalImg(string _filename)
{
	// Read input image
	g_image1 = imread(_filename);
	if (!g_image1.data)
	{
		/*printf("讀取%s失敗!", _filename);*/
		cout << "讀取" + _filename + "失敗!" << endl;
		system("pause");
		return;
	}
	// Display the color image
	/*cv::resize(g_image1, g_image1, cv::Size(), 0.7, 1);*/
	cv::namedWindow("Original Image1");
	cv::imshow("Original Image1", g_image1);
}
/*獲取目標像素*/
void GetObjectPix(Mat _src)
{
	// Identify image pixels with object
	cv::cvtColor(_src, g_binary, COLOR_BGRA2GRAY);
	cv::threshold(g_binary, g_binary, 30, 255, CV_THRESH_BINARY|THRESH_OTSU);//閾值分割原圖的灰度圖,獲得二值圖像
	// Display the binary image
	/*cv::namedWindow("binary Image1");
	cv::imshow("binary Image1", g_binary);*/

	// CLOSE operation
	cv::Mat element5(5, 5, CV_8U, cv::Scalar(1));//5*5正方形,8位uchar型,全1結構元素
	cv::morphologyEx(g_binary, g_fg1, cv::MORPH_CLOSE, element5, Point(-1, -1), 2);// 閉運算填充物體內細小空洞、連接鄰近物體

	// Display the foreground image
	cv::namedWindow("Foreground Image");
	cv::imshow("Foreground Image", g_fg1);
}
/*標記背景和未知區域*/
bool MarkBkgUkg(Mat _binary)
{
	// Identify image pixels without objects

	cv::dilate(_binary, g_bg1, cv::Mat(), cv::Point(-1, -1), 5);//膨脹4次,錨點爲結構元素中心點
	cv::threshold(g_bg1, g_bg1, 1, 128, cv::THRESH_BINARY_INV);//>=1的像素設置爲128(即背景)
	// Display the background image
	cv::namedWindow("Background Image");
	cv::imshow("Background Image", g_bg1);
	return true;
}
bool GetMarker(Mat _fg,Mat _bg)
{
	//Get markers image

	g_markers1 = _fg + _bg; //使用Mat類的重載運算符+來合併圖像。
	cv::namedWindow("markers Image");
	cv::imshow("markers Image", g_markers1);
	return true;
}
void SegmentImg(Mat _src)
{
	// Apply watershed segmentation

	WatershedSegmenter segmenter1;  //實例化一個分水嶺分割方法的對象
	segmenter1.setMarkers(g_markers1);//設置算法的標記圖像,使得水淹過程從這組預先定義好的標記像素開始
	segmenter1.process(_src);     //傳入待分割原圖

	// Display segmentation result
	cv::namedWindow("Segmentation1");
	cv::imshow("Segmentation1", segmenter1.getSegmentation());//將修改後的標記圖markers轉換爲可顯示的8位灰度圖並返回分割結果(白色爲前景,灰色爲背景,0爲邊緣)
	waitKey();
	// Display watersheds
	cv::namedWindow("Watersheds1");
	cv::imshow("Watersheds1", segmenter1.getWatersheds());//以圖像的形式返回分水嶺(分割線條)
}

/*找到最大連通區域*/
void FindMaxArea(Mat _binnary)
{
	vector<vector<cv::Point>> contours;
	findContours(g_binary, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);
	// 尋找最大連通域
	double maxArea = 0;
	vector<cv::Point> maxContour;
	for (size_t i = 0; i < contours.size(); i++)
	{
		double area = cv::contourArea(contours[i]);
		if (area > maxArea)
		{
			maxArea = area;
			maxContour = contours[i];
		}
	}
	// 將輪廓轉爲矩形框
	g_maxRect = cv::boundingRect(maxContour);
	g_maxRect.width += 10;
	g_maxRect.height += 10;
	g_maxRect.x -= 5;
	g_maxRect.y -= 5;
	cout << "Corner 1 recorded at (" << g_maxRect.x << "," << g_maxRect.y <<")"<< endl;
	cout << "Corner 2 recorded at (" << g_maxRect.x + g_maxRect.width << "," << g_maxRect.y + g_maxRect.height << ")" << endl;
	Mat local_img = g_image1.clone();
	rectangle(local_img, g_maxRect, Scalar(0, 0, 255), 1);
	namedWindow("MaxArea");
	imshow("MaxArea", local_img);
}
/*利用圖割算法進行分割*/
void MyGrabCut(Mat _src,Rect _Box)
{
	grabCut(_src, mask, _Box, bgModel, fgModel, 5, cv::GC_INIT_WITH_RECT);
	compare(mask, GC_PR_FGD, mask, cv::CMP_EQ);
	namedWindow("Crop");
	Mat imageROI;
	imageROI.create(_src.size(), _src.type());
	/*imageROI = img(Rect(box));*/
	imageROI.setTo(Scalar(255, 255, 255));
	_src.copyTo(imageROI, mask);
	Mat crop(imageROI, _Box);
	imshow("Crop", crop);
}
int main()
{
	ReadOriginalImg("flower.jpg");
	GetObjectPix(g_image1);
	/*MarkBkgUkg(g_binary);
	GetMarker(g_fg1, g_bg1);
	SegmentImg(g_image1);*/
	FindMaxArea(g_fg1);
	MyGrabCut(g_image1, g_maxRect);
	waitKey(0);
}

實現結果

實驗用圖像

程序有點慢,大約53s.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章