上一篇文章中介紹了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.