OpenCV3.0 Examples学习笔记(10)-grabcut.cpp-grabcut函数实现人机交互高效分割图像前景背景
2017-01-12 13:18
771 查看
这个系列的目的是通过对OpenCV示例,进一步了解OpenCV函数的使用,不涉及具体原理。
目录
简介
Example运行截图
Example分析
Example代码
简介
本文记录了对OpenCV示例grabcut.cpp的分析。
资料地址:http://docs.opencv.org/3.0.0/de/dd0/grabcut_8cpp-example.html
这个示例主要演示了如何使用grabcut对图像进行分割。
示例涉及到
setMouseCallback,onMouse,grabCut等函数
具体如下:
Example截图
Example分析
1.主函数
1.1从命令行参数加载图像
if( argc!=2 )
{
help();
return 1;
}
string filename = argv[1];
if( filename.empty() )
{
cout << "\nDurn, couldn't read in " << argv[1] << endl;
return 1;
}
Mat image = imread( filename, 1 );
if( image.empty() )
{
cout << "\n Durn, couldn't read image filename " << filename << endl;
return 1;
}
1.2创建预览窗口
const string winName = "image";
//namedWindow( winName, WINDOW_AUTOSIZE );
namedWindow( winName, WINDOW_NORMAL );
注意:
(1)这一次的预览窗口还支持使用鼠标绘图。
1.3设置鼠标回调
setMouseCallback( winName, on_mouse, 0 );
1.4为GCApplication对象gcapp设置参数
gcapp.setImageAndWinName( image, winName );
gcapp.showImage();
注意:
(1)实际上示例中几乎所有grabcut极其相关的人机交互操作(鼠标/键盘)都是在GCApplication类完成。
1.5通过键盘录入修改参数
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;
}
}
注意:
(1)Esc: 退出
(2)r:返回原图
(3)n:根据设置调用grabcut
1.6释放窗口
destroyWindow( winName );
2.具体分析示例最重要的GCApplication
2.1初始化所有参数至调用grabcut前
reset
2.2设置图像和预览窗口
setImageAndWinName
2.2显示预览图
showImage
2.3鼠标响应事件,用于人机交互设置grabcut,rect位置
mouseClick
2.4调用grabcut
nextIter
2.5返回处理次数
getIterCount
Example代码
参考资料:
1.《【OpenCV】 GrabCut.cpp详解》
2.《学习OpenCV——学习grabcut算法》
3.《Opencv之鼠标响应setMouseCallback()的用法》
目录
简介
Example运行截图
Example分析
Example代码
简介
本文记录了对OpenCV示例grabcut.cpp的分析。
资料地址:http://docs.opencv.org/3.0.0/de/dd0/grabcut_8cpp-example.html
这个示例主要演示了如何使用grabcut对图像进行分割。
示例涉及到
setMouseCallback,onMouse,grabCut等函数
具体如下:
setMouseCallback 为窗口创建鼠标回调事件。 函数原型: void setMousecallback(const string& winname, MouseCallback onMouse, void* userdata=0) 函数参数说明: winname:窗口的名字 onMouse:鼠标响应函数,回调函数。指定窗口里每次鼠标时间发生的时候,被调用的函数 指针。 这个函数的原型应该为: void on_Mouse(int event, int x, int y, int flags, void* param); userdate:传给回调函数的参数 |
MouseCallback onMouse 鼠标回调函数,响应鼠标消息(如鼠标移动,鼠标左键按下,左键抬起等)。 函数原型: void on_Mouse(int event, int x, int y, int flags, void* param); 函数参数说明: event:CV_EVENT_*变量之一 x,y:鼠标指针在图像坐标系的坐标(不是窗口坐标系) flags:CV_EVENT_FLAG的组合 param:用户定义的传递到setMouseCallback函数调用的参数。 常用的event: #defineCV_EVENT_MOUSEMOVE #defineCV_EVENT_LBUTTONDOWN #defineCV_EVENT_RBUTTONDOWN #defineCV_EVENT_LBUTTONUP #defineCV_EVENT_RBUTTONUP 和标志位flags有关的: #defineCV_EVENT_FLAG_LBUTTON 注意:flags & CV_EVENT_FLAG_LBUTTON 的意思是 提取flags的CV_EVENT_FLAG_LBUTTON 标志位,!()的意思是 标志位无效 例如if (event == EVENT_MOUSEMOVE && (flags&EVENT_FLAG_LBUTTON)) ,表示鼠标移动且左键按下 |
grabCut OpenCV中grabcut函数基于《"GrabCut" - Interactive Foreground Extraction using Iterated Graph Cuts》这篇文章来实现的。该算法利用了图像中的纹理(颜色)信息和边界(反差)信息,只要少量的用户交互操作即可得到比较好的分割结果。如果前景和背景之间的颜色反差不大,分割的效果不好;不过,这种情况下允许手工标记一些前景或背景区域,这样能得到较好的结果。 以上关于grabcut算法背景介绍摘抄至参考资料2.《学习OpenCV——学习grabcut算法》 函数原型: 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中,然后再传入grabCu t函数;在处理结束之后,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),执行分割。 |
1.主函数
1.1从命令行参数加载图像
if( argc!=2 )
{
help();
return 1;
}
string filename = argv[1];
if( filename.empty() )
{
cout << "\nDurn, couldn't read in " << argv[1] << endl;
return 1;
}
Mat image = imread( filename, 1 );
if( image.empty() )
{
cout << "\n Durn, couldn't read image filename " << filename << endl;
return 1;
}
1.2创建预览窗口
const string winName = "image";
//namedWindow( winName, WINDOW_AUTOSIZE );
namedWindow( winName, WINDOW_NORMAL );
注意:
(1)这一次的预览窗口还支持使用鼠标绘图。
1.3设置鼠标回调
setMouseCallback( winName, on_mouse, 0 );
1.4为GCApplication对象gcapp设置参数
gcapp.setImageAndWinName( image, winName );
gcapp.showImage();
注意:
(1)实际上示例中几乎所有grabcut极其相关的人机交互操作(鼠标/键盘)都是在GCApplication类完成。
1.5通过键盘录入修改参数
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;
}
}
注意:
(1)Esc: 退出
(2)r:返回原图
(3)n:根据设置调用grabcut
1.6释放窗口
destroyWindow( winName );
2.具体分析示例最重要的GCApplication
2.1初始化所有参数至调用grabcut前
reset
2.2设置图像和预览窗口
setImageAndWinName
2.2显示预览图
showImage
2.3鼠标响应事件,用于人机交互设置grabcut,rect位置
mouseClick
2.4调用grabcut
nextIter
2.5返回处理次数
getIterCount
Example代码
#include "opencv2/imgcodecs.hpp" #include "opencv2/highgui/highgui.hpp" #include "opencv2/imgproc/imgproc.hpp" #include <iostream> using namespace std; using namespace cv; static void help() { cout << "\nThis program demonstrates GrabCut segmentation -- select an object in a region\n" "and then grabcut will attempt to segment it out.\n" "Call:\n" "./grabcut <image_name>\n" "\nSelect a rectangular area around the object you want to segment\n" << "\nHot keys: \n" "\tESC - quit the program\n" "\tr - restore the original image\n" "\tn - next iteration\n" "\n" "\tleft mouse button - set rectangle\n" "\n" "\tCTRL+left mouse button - set GC_BGD pixels\n" "\tSHIFT+left mouse button - set GC_FGD pixels\n" "\n" "\tCTRL+right mouse button - set GC_PR_BGD pixels\n" "\tSHIFT+right mouse button - set GC_PR_FGD pixels\n" << endl; } 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 = EVENT_FLAG_CTRLKEY; const int FGD_KEY = EVENT_FLAG_SHIFTKEY; static void getBinMask( const Mat& comMask, Mat& binMask ) { if( comMask.empty() || comMask.type()!=CV_8UC1 ) CV_Error( Error::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() { CV_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 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 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 EVENT_LBUTTONUP: if( rectState == IN_PROCESS ) { rect = Rect( Point(rect.x, rect.y), Point(x,y) ); rectState = SET; setRectInMask(); CV_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 EVENT_RBUTTONUP: if( prLblsState == IN_PROCESS ) { setLblsInMask(flags, Point(x,y), true); prLblsState = SET; showImage(); } break; case EVENT_MOUSEMOVE: if( rectState == IN_PROCESS ) { rect = Rect( Point(rect.x, rect.y), Point(x,y) ); CV_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 ); } int main( int argc, char** argv ) { if( argc!=2 ) { help(); return 1; } string filename = argv[1]; if( filename.empty() ) { cout << "\nDurn, couldn't read in " << argv[1] << 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.《【OpenCV】 GrabCut.cpp详解》
2.《学习OpenCV——学习grabcut算法》
3.《Opencv之鼠标响应setMouseCallback()的用法》
相关文章推荐
- 利用OpenCV的Grabcut()函数实现图像的前景与背景的分割-并对Grabcut()作详细介绍
- 图像处理(十四)图像分割(4)grab cut的图割实现-Siggraph 2004
- GrabCut in One Cut(基于图割算法grabcut的一次快速图像分割的OpenCV实现)----目前效果最好的图割
- opencv实现图像分割,分离前景和背景(1)
- 【图像处理】图像分割之(一~四)GraphCut,GrabCut函数使用和源码解读(OpenCV)
- OpenCV3.0 Examples学习笔记(11)-houghcircles.cpp-houghcircles函数实现圆形检测
- GrabCut in One Cut(基于图割算法grabcut的一次快速图像分割的OpenCV实现)----目前效果最好的图割
- 第九章 – 图像局部与分割 – 背景场景和分割前景目标的方法(平均背景法)(例程待实现)
- 图像分割之(六)CUDA实现GrabCut
- GrabCut in One Cut(基于图割算法grabcut的一次快速图像分割的OpenCV实现)----目前效果最好的图割
- OpenCV3.0 Examples学习笔记(13)-kmeans.cpp-kmeans函数实现对图像位置进行聚类
- opencv实现图像分割,分离前景和背景(2)
- 图像处理(十四)图像分割(4)grab cut的图割实现-Siggraph 2004
- OpenCV图像分割cv::grabCut()函数
- 图像分割之(五)OpenCV的GrabCut调用
- Python OpenCV学习笔记之:使用Grabcut算法进行图像背景和前景分割
- 图像分割GrabCut
- 【图像识别】GrabCut的Android实现
- OpenCV(EmguCV)2.1新特性介绍之图像分割GrabCut
- OpenCV实现图像物体轮廓,前景背景,标记,并保存。