grabCut函数
函數原型:void grabCut(InputArray img, InputOutputArray mask, Rect rect, InputOutputArray bgdModel, InputOutputArray fgdModel, int iterCount, int mode=GC_EVAL )img:待分割的源圖像,必須是8位3通道(CV_8UC3)圖像,在處理的過程中不會被修改;mask:掩碼圖像,大小和原圖像一致。可以有如下幾種取值:GC_BGD(=0),背景;GC_FGD(=1),前景;GC_PR_BGD(=2),可能的背景;GC_PR_FGD(=3),可能的前景。rect:用于限定需要進行分割的圖像范圍,只有該矩形窗口內的圖像部分才被處理;bgdModel:背景模型,如果為null,函數內部會自動創建一個bgdModel;fgdModel:前景模型,如果為null,函數內部會自動創建一個fgdModel;iterCount:迭代次數,必須大于0;mode:用于指示grabCut函數進行什么操作。可以有如下幾種選擇:GC_INIT_WITH_RECT(=0),用矩形窗初始化GrabCut;GC_INIT_WITH_MASK(=1),用掩碼圖像初始化GrabCut;GC_EVAL(=2),執行分割。
4、基本原理:首先用戶在圖片上畫一個方框,grabCut默認方框內部為前景,設置掩碼為2,方框外部都是背景,設置掩碼為0。然后根據算法,將方框內部檢查出來是背景的位置,掩碼由2改為0。最后,經過算法處理,方框中掩碼依然為2的,就是檢查出來的前景,其他為背景。
1、首先是裝載需要處理的源圖片。 <pre name="code" class="cpp">filename = argv[1];image = imread( filename, 1 );
2、設置掩碼,首先創建了一個和源圖片一樣大小的掩碼空間。接著將整個掩碼空間設置為背景:GC_BGD。接著創建了一個rect,對應左上角坐標為: (110,220),長寬都為100。接著在掩碼空間mask對應左邊位置的掩碼設置為GC_PR_FGD(疑似為前景)。這個rect就是需要分離前景背景的空間。同時 在源圖像上,rect對應的需要被處理位置畫出綠色方框框選。接著將畫了綠色方框之后的源圖片顯示出來。 <pre name="code" class="cpp">mask.create(image.size(), CV_8UC1);mask.setTo(GC_BGD);setRectInMask();(mask(rect)).setTo(Scalar(GC_PR_FGD));rectangle(image, Point(rect.x, rect.y), Point(rect.x + rect.width, rect.y + rect.height ), GREEN, 2);imshow(winName, image);
3、之前的源圖像被畫了綠色方框,所以需要重新裝載一遍源圖像。接著使用函數grabCut,根據傳入的相關參數,進行前景背景分離操作。最后在生成的 結果保存在mask中,背景被置為0,前景被置為1。接著將mask結果篩選到binMask中。最后使用image.copyTo(res, binMask);將原圖像根據binMask作為掩碼, 將篩選出來的前景復制到目標圖像res中。并將目標圖像顯示出來。
實例講解1
這些例子都主要是根據opencv自帶的例子:opencv\samples\cpp\grabcut.cpp 簡化修改而來。源代碼
代碼如下: <pre name="code" class="cpp">#include "opencv2/highgui/highgui.hpp" #include "opencv2/imgproc/imgproc.hpp"#include <iostream>using namespace std; using namespace cv; string filename; Mat image; string winName = "show"; Rect rect; Mat mask; const Scalar GREEN = Scalar(0,255,0); Mat bgdModel, fgdModel;void setRectInMask(){rect.x = 110;rect.y = 220;rect.width = 100;rect.height = 100; }static void getBinMask( const Mat& comMask, Mat& binMask ){binMask.create( comMask.size(), CV_8UC1 );binMask = comMask & 1; }int main(int argc, char* argv[]){Mat binMask, res;filename = argv[1];image = imread( filename, 1 );mask.create(image.size(), CV_8UC1);mask.setTo(GC_BGD);setRectInMask();(mask(rect)).setTo(Scalar(GC_PR_FGD));rectangle(image, Point(rect.x, rect.y), Point(rect.x + rect.width, rect.y + rect.height ), GREEN, 2);imshow(winName, image);image = imread( filename, 1 );grabCut(image, mask, rect, bgdModel, fgdModel, 1, GC_INIT_WITH_RECT);getBinMask(mask, binMask);image.copyTo(res, binMask);imshow("result", res);waitKey(0);return 0; }1、首先是裝載需要處理的源圖片。 <pre name="code" class="cpp">filename = argv[1];image = imread( filename, 1 );
2、設置掩碼,首先創建了一個和源圖片一樣大小的掩碼空間。接著將整個掩碼空間設置為背景:GC_BGD。接著創建了一個rect,對應左上角坐標為: (110,220),長寬都為100。接著在掩碼空間mask對應左邊位置的掩碼設置為GC_PR_FGD(疑似為前景)。這個rect就是需要分離前景背景的空間。同時 在源圖像上,rect對應的需要被處理位置畫出綠色方框框選。接著將畫了綠色方框之后的源圖片顯示出來。 <pre name="code" class="cpp">mask.create(image.size(), CV_8UC1);mask.setTo(GC_BGD);setRectInMask();(mask(rect)).setTo(Scalar(GC_PR_FGD));rectangle(image, Point(rect.x, rect.y), Point(rect.x + rect.width, rect.y + rect.height ), GREEN, 2);imshow(winName, image);
3、之前的源圖像被畫了綠色方框,所以需要重新裝載一遍源圖像。接著使用函數grabCut,根據傳入的相關參數,進行前景背景分離操作。最后在生成的 結果保存在mask中,背景被置為0,前景被置為1。接著將mask結果篩選到binMask中。最后使用image.copyTo(res, binMask);將原圖像根據binMask作為掩碼, 將篩選出來的前景復制到目標圖像res中。并將目標圖像顯示出來。
效果演示
原圖像: 目標圖像:
實例講解2
繼續這個例子中加入了鼠標的操作。源代碼
<pre name="code" class="cpp">#include "opencv2/highgui/highgui.hpp" #include "opencv2/imgproc/imgproc.hpp"#include <iostream>using namespace std; using namespace cv; string filename; Mat image; string winName = "show"; enum{NOT_SET = 0, IN_PROCESS = 1, SET = 2}; uchar rectState; Rect rect; Mat mask; const Scalar GREEN = Scalar(0,255,0); Mat bgdModel, fgdModel;void setRectInMask(){rect.x = max(0, rect.x);rect.y = max(0, rect.y);rect.width = min(rect.width, image.cols-rect.x);rect.height = min(rect.height, image.rows-rect.y);}static void getBinMask( const Mat& comMask, Mat& binMask ){binMask.create( comMask.size(), CV_8UC1 );binMask = comMask & 1; }void on_mouse( int event, int x, int y, int flags, void* ) {Mat res;Mat binMask;switch( event ){case CV_EVENT_LBUTTONDOWN:if( rectState == NOT_SET){rectState = IN_PROCESS;rect = Rect( x, y, 1, 1 );}break;case CV_EVENT_LBUTTONUP:if( rectState == IN_PROCESS ){rect = Rect( Point(rect.x, rect.y), Point(x,y) );rectState = SET;(mask(rect)).setTo( Scalar(GC_PR_FGD));image = imread( filename, 1 );grabCut(image, mask, rect, bgdModel, fgdModel, 1, GC_INIT_WITH_RECT);getBinMask( mask, binMask );image.copyTo(res, binMask );imshow("11", res);}break;case CV_EVENT_MOUSEMOVE:if( rectState == IN_PROCESS ){rect = Rect( Point(rect.x, rect.y), Point(x,y) );image = imread( filename, 1 );rectangle(image, Point( rect.x, rect.y ), Point(rect.x + rect.width, rect.y + rect.height ), GREEN, 2);imshow(winName, image);}break;} }int main(int argc, char* argv[]){filename = argv[1];image = imread( filename, 1 );imshow(winName, image);mask.create(image.size(), CV_8UC1);rectState = NOT_SET;mask.setTo(GC_BGD);setMouseCallback(winName, on_mouse, 0);waitKey(0);return 0; }代碼講解
這一步中,主要也就是加入了鼠標響應。通過鼠標左鍵按下拖動,來框選之前提到的綠色目標框,當釋放掉鼠標左鍵之后,便會將框選的位置進行 背景分離。總結
- 上一篇: Watershed函数
- 下一篇: opencv矩阵运算(1)