OpenCV學習(20) grabcut分割算法


      在OpenCV中,實現了grabcut分割算法,該算法可以方便的分割出前景圖像,操作簡單,而且分割的效果很好。算法的原理參見papaer:“GrabCut” — Interactive Foreground Extraction using Iterated Graph Cuts

比如下面的一副圖,我們只要選定一個四邊形框,把框中的圖像作為grabcut的一個輸入參數,表示該框中的像素可能屬於前景,但框外的部分一定屬於背景

imageimage

然后調用grabcut函數,就可以分割出城堡來。具體代碼如下:

// 打開另一幅圖像
cv::Mat image= cv::imread("../tower.jpg");
if (!image.data)
{
cout<<"不能打開圖像!"<<endl;
return 0;
}

// 矩形外的像素是背景
cv::Rect rectangle(50,70,image.cols-150,image.rows-180);

cv::Mat result;
//兩個臨時矩陣變量,作為算法的中間變量使用,不用care
cv::Mat bgModel,fgModel;
double tt = cv::getTickCount();
// GrabCut 分段
cv::grabCut(image, //輸入圖像
result, //分段結果
rectangle,// 包含前景的矩形
bgModel,fgModel, // 前景、背景
1, // 迭代次數
cv::GC_INIT_WITH_RECT); // 用矩形
tt = cv::getTickCount() - tt;
printf("算法執行執行時間:%g ms\n", tt/cv::getTickFrequency()*1000);
// 得到可能是前景的像素
//比較函數保留值為GC_PR_FGD的像素
cv::compare(result,cv::GC_PR_FGD,result,cv::CMP_EQ);
// 產生輸出圖像
cv::Mat foreground(image.size(),CV_8UC3,cv::Scalar(255,255,255));
//背景值為 GC_BGD=0,作為掩碼
image.copyTo(foreground,result);

grabCut函數的第一個參數為我們要處理的圖像,本程序中就是image,圖像的類型必須為:CV_8UC3

第二個參數是mask圖像,它的大小和image一樣,但是它的格式為CV_8UC1,只能是單通道的,grabcut算法的結果就保存在該圖像中。

前面的代碼中,我們並沒有對mask圖像(result)進行初始化設置,因為第6個參數為cv::GC_INIT_WITH_RECT,它表示算法會根據rectangle的范圍,來生成一個初始化的mask圖像。

cv::grabCut(image,    //輸入圖像
    result,   //分段結果
    rectangle, // 包含前景的矩形
    bgModel,fgModel, // 前景、背景
    1,        // 迭代次數
    cv::GC_INIT_WITH_RECT); // 用矩形

mask圖像的值只能為下面下面4個值(PR,probably表示可能的):

GC_BGD    = 0,  //背景

GC_FGD    = 1,  //前景
GC_PR_BGD = 2,  //可能背景

GC_PR_FGD = 3   //可能前景

根據rectangle生成的mask圖像規則為:四邊形外面的部分一定是背景,所以在mask圖中對應的像素值為GC_BGD,而四邊形內部的的值可能為前景,所以對應的像素值為GC_PR_FGD。所以我們程序中使用mask圖像應該如下圖所示。


image

如果第7個參數為GC_INIT_WITH_MASK,這時第三個參數rectangle沒有使用,我們必須在調用grabcut函數之前,手工設置mask圖像(變量result),如果我們把result設置成上圖所示的灰度圖。那個調用函數

cv::grabCut(image1,    //輸入圖像
    result1,   //分段結果
    rectangle, // 包含前景的矩形
    bgModel,fgModel, // 前景、背景
    1,        // 迭代次數
    cv::GC_INIT_WITH_MASK); // 用矩形

可以得到同樣的結果。
可以參考下面的代碼:

cv::Mat result1= cv::Mat(image1.rows, image1.cols,CV_8UC1, cv::Scalar(cv::GC_BGD));
//注意給子矩陣賦值的方法
cv::Mat roi(result1, cv::Rect(50,70,result1.cols-150,result.rows-180));
roi = cv::Scalar(cv::GC_PR_FGD);
tt = cv::getTickCount();
// GrabCut 分段
cv::grabCut(image1, //輸入圖像
result1, //分段結果
rectangle,// 包含前景的矩形
bgModel,fgModel, // 前景、背景
1, // 迭代次數
cv::GC_INIT_WITH_MASK); // 用矩形
tt = cv::getTickCount() - tt;
printf("算法執行執行時間:%g ms\n", tt/cv::getTickFrequency()*1000);

// 得到可能是前景的像素
//比較函數保留值為GC_PR_FGD的像素
cv::compare(result1,cv::GC_PR_FGD,result,cv::CMP_EQ);
// 產生輸出圖像
cv::Mat foreground1(image1.size(),CV_8UC3,cv::Scalar(255,255,255));
//背景值為 GC_BGD=0,作為掩碼
image.copyTo(foreground1,result1);

第3個參數是rectangle的大小位置,如果第7個參數為GC_INIT_WITH_MASK,則該參數沒有作用。

第4,5個參數是兩個算法在執行過程中使用臨時矩陣變量,不用care它們的內容。

第6個參數是迭代次數,迭代越多,效果越好,但划時間也越長。

第7個參數是操作模式,通常情況下為GC_INIT_WITH_RECT和GC_INIT_WITH_MASK。

從上面的圖中,我們可以看到,grabcut算法的效果很好,但是花的時間也很長,上面圖像在我的筆記本上需要4.4秒。

image

程序源代碼:工程FirstOpenCV13


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM