OpenCV  4.10.0
开源计算机视觉库
正在加载...
正在搜索...
没有匹配项
samples/cpp/grabcut.cpp

使用 GrabCut 算法的示例

#include <iostream>
using namespace std;
using namespace cv;
static void help(char** argv)
{
cout << "\n本程序演示了 GrabCut 分割 -- 在一个区域内选择一个物体\n"
"然后 GrabCut 将尝试将其分割出来。\n"
"调用方式:\n"
<< argv[0] << " <图像文件名>\n"
"\n在您要分割的物体周围选择一个矩形区域\n" <<
"\n热键:\n"
"\tESC - 退出程序\n"
"\tr - 恢复原始图像\n"
"\tn - 下一次迭代\n"
"\n"
"\t左键 - 设置矩形\n"
"\n"
"\tCTRL+左键 - 设置 GC_BGD 像素\n"
"\tSHIFT+左键 - 设置 GC_FGD 像素\n"
"\n"
"\tCTRL+右键 - 设置 GC_PR_BGD 像素\n"
"\tSHIFT+右键 - 设置 GC_PR_FGD 像素\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 为空或类型不正确(不是 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 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;
image->copyTo( res );
if( isInitialized ){
getBinMask( mask, binMask);
Mat black (binMask.rows, binMask.cols, CV_8UC3, cv::Scalar(0,0,0));
black.setTo(Scalar::all(255), binMask);
addWeighted(black, 0.5, res, 0.5, 0.0, res);
}
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 添加错误参数检查
switch( event )
{
case EVENT_LBUTTONDOWN: // 设置矩形或GC_BGD(GC_FGD)标签
{
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: // 设置GC_PR_BGD(GC_PR_FGD)标签
{
bool isb = (flags & BGD_KEY) != 0,
isf = (flags & FGD_KEY) != 0;
if ( (isb || isf) && rectState == SET )
prLblsState = IN_PROCESS;
}
break;
if( rectState == IN_PROCESS )
{
if(rect.x == x || rect.y == y){
rectState = NOT_SET;
}
else{
rect = Rect( Point(rect.x, rect.y), Point(x,y) );
rectState = SET;
setRectInMask();
CV_Assert( bgdPxls.empty() && fgdPxls.empty() && prBgdPxls.empty() && prFgdPxls.empty() );
}
}
if( lblsState == IN_PROCESS )
{
setLblsInMask(flags, Point(x,y), false);
lblsState = SET;
nextIter();
}
else{
if(rectState == SET){
nextIter();
}
}
break;
if( prLblsState == IN_PROCESS )
{
setLblsInMask(flags, Point(x,y), true);
prLblsState = SET;
}
if(rectState == SET){
nextIter();
}
break;
if( rectState == IN_PROCESS )
{
rect = Rect( Point(rect.x, rect.y), Point(x,y) );
CV_Assert( bgdPxls.empty() && fgdPxls.empty() && prBgdPxls.empty() && prFgdPxls.empty() );
}
else if( lblsState == IN_PROCESS )
{
setLblsInMask(flags, Point(x,y), false);
}
else if( prLblsState == IN_PROCESS )
{
setLblsInMask(flags, Point(x,y), true);
}
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 )
{
cv::CommandLineParser parser(argc, argv, "{@input| messi5.jpg |}");
help(argv);
string filename = parser.get<string>("@input");
if( filename.empty() )
{
cout << "\nDurn, empty filename" << endl;
return 1;
}
Mat image = imread(samples::findFile(filename), IMREAD_COLOR);
if( image.empty() )
{
cout << "\n Durn, couldn't read image filename " << filename << endl;
return 1;
}
const string winName = "image";
namedWindow( winName, WINDOW_AUTOSIZE );
setMouseCallback( winName, on_mouse, 0 );
gcapp.setImageAndWinName( image, winName );
gcapp.showImage();
for(;;)
{
char c = (char)waitKey(0);
switch( 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;
}
用于命令行解析。
定义 utility.hpp:820
n 维密集数组类
定义 mat.hpp:812
MatSize size
定义 mat.hpp:2160
void copyTo(OutputArray m) const
将矩阵复制到另一个矩阵。
void create(int rows, int cols, int type)
如果需要,分配新的数组数据。
int cols
定义 mat.hpp:2138
bool empty() const
如果数组没有元素,则返回 true。
int rows
行和列的数量,或者当矩阵具有超过 2 个维度时为 (-1, -1)
定义 mat.hpp:2138
int type() const
返回矩阵元素的类型。
二维矩形的模板类。
定义 types.hpp:444
void min(InputArray src1, InputArray src2, OutputArray dst)
计算两个数组或数组和标量的逐元素最小值。
void max(InputArray src1, InputArray src2, OutputArray dst)
计算两个数组或数组和标量的逐元素最大值。
void addWeighted(InputArray src1, double alpha, InputArray src2, double beta, double gamma, OutputArray dst, int dtype=-1)
计算两个数组的加权和。
unsigned char uchar
定义 interface.h:51
#define CV_8UC1
定义 interface.h:88
#define CV_8UC3
定义 interface.h:90
#define CV_Error(code, msg)
调用错误处理程序。
定义 base.hpp:320
#define CV_Assert(expr)
在运行时检查条件,如果失败则抛出异常。
定义 base.hpp:342
@ circle
定义 gr_skig.hpp:62
GMat mask(const GMat &src, const GMat &mask)
将掩码应用于矩阵。
@ EVENT_LBUTTONUP
表示左键已释放。
定义 highgui.hpp:171
@ EVENT_MOUSEMOVE
表示鼠标指针已移至窗口上。
定义 highgui.hpp:167
@ EVENT_RBUTTONDOWN
表示右键已按下。
定义 highgui.hpp:169
@ EVENT_RBUTTONUP
表示右键已释放。
定义 highgui.hpp:172
@ EVENT_LBUTTONDOWN
表示左键已按下。
定义 highgui.hpp:168
void imshow(const String &winname, InputArray mat)
在指定窗口中显示图像。
int waitKey(int delay=0)
等待按下的键。
void namedWindow(const String &winname, int flags=WINDOW_AUTOSIZE)
创建窗口。
void destroyWindow(const String &winname)
销毁指定的窗口。
void setMouseCallback(const String &winname, MouseCallback onMouse, void *userdata=0)
为指定的窗口设置鼠标处理程序。
CV_EXPORTS_W Mat imread(const String &filename, int flags=IMREAD_COLOR)
从文件加载图像。
void rectangle(InputOutputArray img, Point pt1, Point pt2, const Scalar &color, int thickness=1, int lineType=LINE_8, int shift=0)
绘制简单的、粗的或填充的正向矩形。
@ GC_FGD
明显的前景(物体)像素
定义 imgproc.hpp:351
@ GC_BGD
明显的背景像素
定义 imgproc.hpp:350
@ GC_PR_FGD
可能的 Vordergrund-Pixel
定义 imgproc.hpp:353
@ GC_PR_BGD
可能的 Hintergrund-Pixel
定义 imgproc.hpp:352
void grabCut(InputArray img, InputOutputArray mask, Rect rect, InputOutputArray bgdModel, InputOutputArray fgdModel, int iterCount, int mode=GC_EVAL)
运行 GrabCut 算法。
int main(int argc, char *argv[])
定义 highgui_qt.cpp:3
与磁盘上的文件关联的文件存储的“黑盒”表示。
定义 core.hpp:102
void showImage(cv::InputArray img, const CallMetaData &data, const char *description, const char *view)
STL 命名空间。