您的位置:首页 > 运维架构

opencv2-4 图像分割grabCut

2015-09-29 10:57 435 查看
opencv2-4 图像分割grabCut
//--------------------------------------【程序说明】-------------------------------------------

// 程序说明:《OpenCV3编程入门》OpenCV2版书本附赠示例程序08

// 程序描述:grabCut图像分割

// 测试所用操作系统: Windows 7 64bit

// 测试所用IDE版本:Visual Studio 2010

// 测试所用OpenCV版本:2.4.9

// 2014年11月 Revised by @浅墨_毛星云

//------------------------------------------------------------------------------------------------

//---------------------------------【头文件、命名空间包含部分】----------------------------

// 描述:包含程序所使用的头文件和命名空间

//------------------------------------------------------------------------------------------------

#include "opencv2/highgui/highgui.hpp"

#include "opencv2/imgproc/imgproc.hpp"

#include <iostream>

using namespace std;

using namespace cv;

//-----------------------------------【全局变量声明部分】---------------------------------------

// 描述:声明全局变量

//---------------------------------------------------------------------------------------------------

const Scalar RED = Scalar(0,0,255);

const Scalar PINK = Scalar(230,130,255);

const Scalar BLUE = Scalar(255,0,0);

const Scalar LIGHTBLUE = Scalar(255,255,160);

const Scalar GREEN = Scalar(0,255,0);

const int BGD_KEY = CV_EVENT_FLAG_CTRLKEY;

const int FGD_KEY = CV_EVENT_FLAG_SHIFTKEY;

//--------------------------------------【help( )函数】--------------------------------------

// 描述:输出一些帮助信息

//----------------------------------------------------------------------------------------------

static void help()

{

cout << "\n此程序演示了OpenCV中GrabCut图像分割的使用。\n"

"程序运行后,我们需要用鼠标圈出需要分割的那部分物体。\n\n\t按键说明如下:\n\n" <<

"\t【ESC】-退出程序\n"

"\t【r】-恢复原始图片\n"

"\t【n】-开始迭代,和进行下一次迭代\n"

"\n"

"\t【鼠标左键】-设置选中矩形区域\n"

"\t【Ctrl+鼠标左键】-设置GC_BGD像素\n"

"\t【Shift+鼠标左键】-设置CG_FGD像素\n"

"\t【Ctrl+鼠标右键】-设置GC_PR_BGD像素\n"

"\t【Shift+鼠标右键】-设置CG_PR_FGD像素\n" << endl;

}

static void getBinMask( const Mat& comMask, Mat& binMask )

{

if( comMask.empty() || comMask.type()!=CV_8UC1 )

CV_Error( CV_StsBadArg, "comMask is empty or has incorrect type (not CV_8UC1)" );

if( binMask.empty() || binMask.rows!=comMask.rows || binMask.cols!=comMask.cols )

binMask.create( comMask.size(), CV_8UC1 );

binMask = comMask & 1;

}

class GCApplication

{

public:

enum{ NOT_SET = 0, IN_PROCESS = 1, SET = 2 };

static const int radius = 2;

static const int thickness = -1;

void reset();

void setImageAndWinName( const Mat& _image, const string& _winName );

void showImage() const;

void mouseClick( int event, int x, int y, int flags, void* param );

int nextIter();

int getIterCount() const { return iterCount; }

private:

void setRectInMask();

void setLblsInMask( int flags, Point p, bool isPr );

const string* winName;

const Mat* image;

Mat mask;

Mat bgdModel, fgdModel;

uchar rectState, lblsState, prLblsState;

bool isInitialized;

Rect rect;

vector<Point> fgdPxls, bgdPxls, prFgdPxls, prBgdPxls;

int iterCount;

};

void GCApplication::reset()

{

if( !mask.empty() )

mask.setTo(Scalar::all(GC_BGD));

bgdPxls.clear(); fgdPxls.clear();

prBgdPxls.clear(); prFgdPxls.clear();

isInitialized = false;

rectState = NOT_SET;

lblsState = NOT_SET;

prLblsState = NOT_SET;

iterCount = 0;

}

void GCApplication::setImageAndWinName( const Mat& _image, const string& _winName )

{

if( _image.empty() || _winName.empty() )

return;

image = &_image;

winName = &_winName;

mask.create( image->size(), CV_8UC1);

reset();

}

void GCApplication::showImage() const

{

if( image->empty() || winName->empty() )

return;

Mat res;

Mat binMask;

if( !isInitialized )

image->copyTo( res );

else

{

getBinMask( mask, binMask );

image->copyTo( res, binMask );

}

vector<Point>::const_iterator it;

for( it = bgdPxls.begin(); it != bgdPxls.end(); ++it )

circle( res, *it, radius, BLUE, thickness );

for( it = fgdPxls.begin(); it != fgdPxls.end(); ++it )

circle( res, *it, radius, RED, thickness );

for( it = prBgdPxls.begin(); it != prBgdPxls.end(); ++it )

circle( res, *it, radius, LIGHTBLUE, thickness );

for( it = prFgdPxls.begin(); it != prFgdPxls.end(); ++it )

circle( res, *it, radius, PINK, thickness );

if( rectState == IN_PROCESS || rectState == SET )

rectangle( res, Point( rect.x, rect.y ), Point(rect.x + rect.width, rect.y + rect.height ), GREEN, 2);

imshow( *winName, res );

}

void GCApplication::setRectInMask()

{

assert( !mask.empty() );

mask.setTo( GC_BGD );

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);

(mask(rect)).setTo( Scalar(GC_PR_FGD) );

}

void GCApplication::setLblsInMask( int flags, Point p, bool isPr )

{

vector<Point> *bpxls, *fpxls;

uchar bvalue, fvalue;

if( !isPr )

{

bpxls = &bgdPxls;

fpxls = &fgdPxls;

bvalue = GC_BGD;

fvalue = GC_FGD;

}

else

{

bpxls = &prBgdPxls;

fpxls = &prFgdPxls;

bvalue = GC_PR_BGD;

fvalue = GC_PR_FGD;

}

if( flags & BGD_KEY )

{

bpxls->push_back(p);

circle( mask, p, radius, bvalue, thickness );

}

if( flags & FGD_KEY )

{

fpxls->push_back(p);

circle( mask, p, radius, fvalue, thickness );

}

}

void GCApplication::mouseClick( int event, int x, int y, int flags, void* )

{

// TODO add bad args check

switch( event )

{

case CV_EVENT_LBUTTONDOWN: // set rect or GC_BGD(GC_FGD) labels

{

bool isb = (flags & BGD_KEY) != 0,

isf = (flags & FGD_KEY) != 0;

if( rectState == NOT_SET && !isb && !isf )

{

rectState = IN_PROCESS;

rect = Rect( x, y, 1, 1 );

}

if ( (isb || isf) && rectState == SET )

lblsState = IN_PROCESS;

}

break;

case CV_EVENT_RBUTTONDOWN: // set GC_PR_BGD(GC_PR_FGD) labels

{

bool isb = (flags & BGD_KEY) != 0,

isf = (flags & FGD_KEY) != 0;

if ( (isb || isf) && rectState == SET )

prLblsState = IN_PROCESS;

}

break;

case CV_EVENT_LBUTTONUP:

if( rectState == IN_PROCESS )

{

rect = Rect( Point(rect.x, rect.y), Point(x,y) );

rectState = SET;

setRectInMask();

assert( bgdPxls.empty() && fgdPxls.empty() && prBgdPxls.empty() && prFgdPxls.empty() );

showImage();

}

if( lblsState == IN_PROCESS )

{

setLblsInMask(flags, Point(x,y), false);

lblsState = SET;

showImage();

}

break;

case CV_EVENT_RBUTTONUP:

if( prLblsState == IN_PROCESS )

{

setLblsInMask(flags, Point(x,y), true);

prLblsState = SET;

showImage();

}

break;

case CV_EVENT_MOUSEMOVE:

if( rectState == IN_PROCESS )

{

rect = Rect( Point(rect.x, rect.y), Point(x,y) );

assert( bgdPxls.empty() && fgdPxls.empty() && prBgdPxls.empty() && prFgdPxls.empty() );

showImage();

}

else if( lblsState == IN_PROCESS )

{

setLblsInMask(flags, Point(x,y), false);

showImage();

}

else if( prLblsState == IN_PROCESS )

{

setLblsInMask(flags, Point(x,y), true);

showImage();

}

break;

}

}

int GCApplication::nextIter()

{

if( isInitialized )

grabCut( *image, mask, rect, bgdModel, fgdModel, 1 );

else

{

if( rectState != SET )

return iterCount;

if( lblsState == SET || prLblsState == SET )

grabCut( *image, mask, rect, bgdModel, fgdModel, 1, GC_INIT_WITH_MASK );

else

grabCut( *image, mask, rect, bgdModel, fgdModel, 1, GC_INIT_WITH_RECT );

isInitialized = true;

}

iterCount++;

bgdPxls.clear(); fgdPxls.clear();

prBgdPxls.clear(); prFgdPxls.clear();

return iterCount;

}

GCApplication gcapp;

static void on_mouse( int event, int x, int y, int flags, void* param )

{

gcapp.mouseClick( event, x, y, flags, param );

}

//-----------------------------------【main( )函数】--------------------------------------------

// 描述:控制台应用程序的入口函数,我们的程序从这里开始

//-------------------------------------------------------------------------------------------------

int main( int argc, char** argv )

{

string filename = "1.jpg";

if( filename.empty() )

{

cout << "\nDurn, couldn't read in "<< endl;

return 1;

}

Mat image = imread( filename, 1 );

if( image.empty() )

{

cout << "\n Durn, couldn't read image filename " << filename << endl;

return 1;

}

help();

const string winName = "image";

namedWindow( winName, WINDOW_AUTOSIZE );

setMouseCallback( winName, on_mouse, 0 );

gcapp.setImageAndWinName( image, winName );

gcapp.showImage();

for(;;)

{

int c = waitKey(0);

switch( (char) c )

{

case '\x1b':

cout << "Exiting ..." << endl;

goto exit_main;

case 'r':

cout << endl;

gcapp.reset();

gcapp.showImage();

break;

case 'n':

int iterCount = gcapp.getIterCount();

cout << "<" << iterCount << "... ";

int newIterCount = gcapp.nextIter();

if( newIterCount > iterCount )

{

gcapp.showImage();

cout << iterCount << ">" << endl;

}

else

cout << "rect must be determined>" << endl;

break;

}

}

exit_main:

destroyWindow( winName );

return 0;

}

程序说明:

1)出错提示:CV_Error( CV_StsBadArg, "comMask is empty or has incorrect type (not CV_8UC1)" );

#define CV_Error( code, msg ) cv::error( cv::Exception(code, msg, "", __FILE__, __LINE__) )宏定义,代表参数分别是出错的代码和错误信息。

CV_StsBadArg= -5, /* function arg/param is bad */

2)函数原型:

void cv::grabCut( const Mat& img, Mat& mask, Rect rect,

Mat& bgdModel, Mat& fgdModel,int iterCount, int mode )

其中:

img——待分割的源图像,必须是8位3通道(CV_8UC3)图像,在处理的过程中不会被修改;

mask——掩码图像,如果使用掩码进行初始化,那么mask保存初始化掩码信息;在执行分割的时候,也可以将用户交互所设定的前景与背景保存到mask中,然后再传入grabCut函数;在处理结束之后,mask中会保存结果。mask只能取以下四种值:

GCD_BGD(=0),背景;

GCD_FGD(=1),前景;

GCD_PR_BGD(=2),可能的背景;

GCD_PR_FGD(=3),可能的前景。

如果没有手工标记GCD_BGD或者GCD_FGD,那么结果只会有GCD_PR_BGD或GCD_PR_FGD;

rect——用于限定需要进行分割的图像范围,只有该矩形窗口内的图像部分才被处理;

bgdModel——背景模型,如果为null,函数内部会自动创建一个bgdModel;bgdModel必须是单通道浮点型(CV_32FC1)图像,且行数只能为1,列数只能为13x5;

fgdModel——前景模型,如果为null,函数内部会自动创建一个fgdModel;fgdModel必须是单通道浮点型(CV_32FC1)图像,且行数只能为1,列数只能为13x5;

iterCount——迭代次数,必须大于0;

mode——用于指示grabCut函数进行什么操作,可选的值有:

GC_INIT_WITH_RECT(=0),用矩形窗初始化GrabCut;

GC_INIT_WITH_MASK(=1),用掩码图像初始化GrabCut;

GC_EVAL(=2),执行分割。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: