上一篇文章中介绍了GrabCut的交互性分割,在这篇文章中,实现了对图像的自动分割。
首先,在GrabCut的交互分割中,我们通过用户自己画框,来框出包含目标的区域。所以,只要我们能够确定这个区域,再使用GrabCut算法就可以分割了。
实现思路
- Otsu算法选择最佳阈值对图像进行二值化,
- 再通过数学形态学处理(可以使得膨胀后的图像比目标大)
- 然后提取最大连通区域(这就获得了我们要的区域)
- 用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.