1. 程式人生 > >opencv實現影象分割,分離前景和背景(1)

opencv實現影象分割,分離前景和背景(1)

簡介

  如題,本篇就是講解和使用opencv函式grabcut,來實現影象前景與背景的分離。

函式原型

  1、opencv官方介紹:opencv官方grabcut介紹
  2、網上童鞋翻譯解釋:學習OpenCV——學習grabcut演算法
  3、大致內容如下:
         函式原型:
              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

  這些例子都主要是根據opencv自帶的例子:opencv\samples\cpp\grabcut.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、首先是裝載需要處理的源圖片。
        filename = argv[1];
	image = imread( filename, 1 );
  2、設定掩碼,首先建立了一個和源圖片一樣大小的掩碼空間。接著將整個掩碼空間設定為背景:GC_BGD。接著建立了一個rect,對應左上角座標為:
(110,220),長寬都為100。接著在掩碼空間mask對應左邊位置的掩碼設定為GC_PR_FGD(疑似為前景)。這個rect就是需要分離前景背景的空間。同時
在源影象上,rect對應的需要被處理位置畫出綠色方框框選。接著將畫了綠色方框之後的源圖片顯示出來。
	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

  繼續這個例子中加入了滑鼠的操作。

原始碼

#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;
}

程式碼講解

  這一步中,主要也就是加入了滑鼠響應。通過滑鼠左鍵按下拖動,來框選之前提到的綠色目標框,當釋放掉滑鼠左鍵之後,便會將框選的位置進行
背景分離。
程式碼下載路徑:http://download.csdn.net/detail/u011630458/8700335