基于GrabCut的自动图像分割

 

上一篇文章中介绍了GrabCut的交互性分割,在这篇文章中,实现了对图像的自动分割。ios

首先,在GrabCut的交互分割中,咱们经过用户本身画框,来框出包含目标的区域。因此,只要咱们可以肯定这个区域,再使用GrabCut算法就能够分割了。算法

实现思路

  1. Otsu算法选择最佳阈值对图像进行二值化,
  2. 再经过数学形态学处理(能够使得膨胀后的图像比目标大)
  3. 而后提取最大连通区域(这就得到了咱们要的区域)
  4. 用GrabCut算法分割

代码

原本想结合分水岭实现图割算法的,因此你会在下面代码段中看到WatershedSegment.h,但实际上并无用到,只是用到了其头文件中的两个函数,因此你必须在main函数中包含这个头文件。否则会报错,固然你也能够把有关图割的代码注释掉,尝试一下分水岭算法,能够参考这篇博文函数

/*WatershedSegment.h*/
#if !defined WATERSHS
#define WATERSHS

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

class WatershedSegmenter {

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;
		//在变换前,把每一个像素p转换为255p+255(在conertTo中实现)
		markers.convertTo(tmp, CV_8U, 255, 255);

		return tmp;
	}
};
#endif





/*Main.cpp*/
#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.ui