做目录网站注意事项,域名被墙污染查询,做搜索引擎优化网站费用,互联网门户网站模板left mouse button - set rectangle SHIFTleft mouse button - set GC_FGD pixels CTRLleft mouse button - set GC_BGD pixels 这段代码是一个使用OpenCV库的GrabCut算法进行图像分割的C程序。它允许用户通过交互式方式选择图像中的一个区域#xff0c;并利用GrabCut算法尝试… left mouse button - set rectangle SHIFTleft mouse button - set GC_FGD pixels CTRLleft mouse button - set GC_BGD pixels 这段代码是一个使用OpenCV库的GrabCut算法进行图像分割的C程序。它允许用户通过交互式方式选择图像中的一个区域并利用GrabCut算法尝试将其分割出来。代码中包含用户操作指南、颜色定义、事件处理以及GrabCut算法的迭代过程实现了用户通过鼠标交互和按键控制来迭代分割效果。程序逻辑结构清晰采用面向对象的方式封装了GrabCut分割相关的操作。 // 引入OpenCV库中处理图像编解码的相关头文件
#include opencv2/imgcodecs.hpp
// 引入OpenCV库中高级GUI的相关头文件
#include opencv2/highgui.hpp
// 引入OpenCV库中图像处理的相关头文件
#include opencv2/imgproc.hpp// 引入输入输出流的库文件
#include iostream// 使用标准命名空间std免去std::前缀
using namespace std;
// 使用OpenCV命名空间cv免去cv::前缀
using namespace cv;// 定义静态辅助函数用于显示程序帮助信息
static void help(char** argv)
{cout \nThis program demonstrates GrabCut segmentation -- select an object in a region\nand then grabcut will attempt to segment it out.\nCall:\n argv[0] image_name\n // 显示如何调用程序\nSelect a rectangular area around the object you want to segment\n// 以下是热键操作说明\nHot keys: \n\tESC - quit the program\n // 按ESC键退出程序\tr - restore the original image\n // 按r键还原到原始图像\tn - next iteration\n // 按n键执行下一迭代步骤\n// 以下是鼠标操作说明\tleft mouse button - set rectangle\n // 左键点击设置矩形区域\n\tCTRLleft mouse button - set GC_BGD pixels\n // 按住CTRL键同时左键点击设置背景像素\tSHIFTleft mouse button - set GC_FGD pixels\n // 按住SHIFT键同时左键点击设置前景像素\n\tCTRLright mouse button - set GC_PR_BGD pixels\n // 按住CTRL键同时右键点击设置可能的背景像素\tSHIFTright mouse button - set GC_PR_FGD pixels\n endl; // 按住SHIFT键同时右键点击设置可能的前景像素
}// 定义一些标量常量表示不同的颜色
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 )
{// 确保comMask非空且类型为CV_8UC1否则抛出错误if( comMask.empty() || comMask.type()!CV_8UC1 )CV_Error( Error::StsBadArg, comMask is empty or has incorrect type (not CV_8UC1) );// 如果binMask为空或大小与comMask不一致则重新创建与comMask大小一致的binMaskif( binMask.empty() || binMask.rows!comMask.rows || binMask.cols!comMask.cols )binMask.create( comMask.size(), CV_8UC1 );// 将comMask中的最低位复制到binMask中binMask comMask 1;
}// 声明GrabCut应用类用于实现GrabCut算法的交互操作
// 定义用于GrabCut算法的应用类
class GCApplication
{
public:// 定义选择状态的枚举类型enum{ NOT_SET 0, IN_PROCESS 1, SET 2 };// 定义圆的半径static const int radius 2;// 定义圆的线条厚度-1表示填充圆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; // 矩形区域vectorPoint fgdPxls, bgdPxls, prFgdPxls, prBgdPxls; // 记录前景和背景像素位置的向量int iterCount; // 迭代计数
};// 成员函数定义
// 重置GCApplication
void GCApplication::reset()
{if( !mask.empty() )mask.setTo(Scalar::all(GC_BGD)); // 如果掩模非空设置掩模的所有值为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(); // 重置GCApplication
}// 展示图像
void GCApplication::showImage() const
{if( image-empty() || winName-empty() )return; // 如果图像为空或窗口名为空直接返回Mat res;Mat binMask;image-copyTo( res ); // 复制图像到resif( 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); // 将黑色图像与原图混合}vectorPoint::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 ); // 显示图像
} // GCApplication类的成员函数设置矩形框到掩模中
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) );
}// GCApplication类的成员函数根据用户输入在掩模上设置前景或背景标签
void GCApplication::setLblsInMask( int flags, Point p, bool isPr )
{vectorPoint *bpxls, *fpxls; // 指向背景和前景像素点的向量uchar bvalue, fvalue; // 背景和前景的像素值// 根据是否是概率图(isPr)设置不同的指针和像素值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;}// flags中对应的按键标志位被设置更新背景像素点向量并在掩模上画圆if( flags BGD_KEY ){bpxls-push_back(p);circle( mask, p, radius, bvalue, thickness );}// flags中对应的按键标志位被设置更新前景像素点向量并在掩模上画圆if( flags FGD_KEY ){fpxls-push_back(p);circle( mask, p, radius, fvalue, thickness );}
}// GCApplication类的成员函数处理鼠标点击事件
void GCApplication::mouseClick( int event, int x, int y, int flags, void* )
{switch( event ){// 左键按下事件设置矩形或者GC_BGD/GC_FGD像素标签case EVENT_LBUTTONDOWN: {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;// 右键按下事件设置GC_PR_BGD/GC_PR_FGD像素标签case EVENT_RBUTTONDOWN: {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 ){// 如果起点和终点一样则不设置矩形框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() );}showImage(); // 显示图像}if( lblsState IN_PROCESS ){setLblsInMask(flags, Point(x,y), false); // 设置像素标签lblsState SET; // 设置标签状态为已设置nextIter(); // 进行一次迭代showImage(); // 显示图像}else{// 如果矩形框状态已设置进行一次迭代后显示图像if(rectState SET){nextIter();showImage();}}break;// 右键释放事件完成概率标签的设置case EVENT_RBUTTONUP:if( prLblsState IN_PROCESS ){setLblsInMask(flags, Point(x,y), true); // 设置概率标签prLblsState SET; // 设置概率标签状态为已设置}// 如果矩形框状态已设置进行一次迭代后显示图像if(rectState SET){nextIter();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 main( int argc, char** argv )
{// 解析命令行参数cv::CommandLineParser parser(argc, argv, {input| messi5.jpg |});// 调用help函数显示帮助信息help(argv);// 获取图片文件名string filename parser.getstring(input);// 如果文件名为空输出错误信息并返回1if( filename.empty() ){cout \nDurn, empty filename endl;return 1;}// 读取图片Mat image imread(samples::findFile(filename), IMREAD_COLOR);// 如果读取失败输出错误信息并返回1if( image.empty() ){cout \n Durn, couldnt read image filename filename endl;return 1;}// 创建窗口const string winName image;namedWindow( winName, WINDOW_AUTOSIZE );// 设置鼠标回调函数setMouseCallback( winName, on_mouse, 0 );// 设置gcapp对象的图片和窗口名称gcapp.setImageAndWinName( image, winName );// 显示图片gcapp.showImage();// 无限循环等待用户按键for (;;){// 等待按键事件char c (char)waitKey(0);switch (c){case \x1b: // 按Esc键退出cout Exiting ... endl;goto exit_main;case r: // 按r键重置cout endl;gcapp.reset(); // 调用重置函数gcapp.showImage(); // 显示图像break;case n: // 按n键进行下一次迭代int iterCount gcapp.getIterCount(); // 获取当前迭代次数cout iterCount ... ;int newIterCount gcapp.nextIter(); // 调用下一个迭代if (newIterCount iterCount){gcapp.showImage(); // 显示新的图像cout iterCount endl;}elsecout rect must be determined endl; // 提示需要先确定矩形框break;}}exit_main:// 销毁窗口并退出destroyWindow( winName );return 0;
} vectorPoint::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); grabCut(*image, mask, rect, bgdModel, fgdModel, 1); // 如果标签已经设置或概率标签已经设置用mask初始化grabCutif (lblsState SET || prLblsState SET)grabCut(*image, mask, rect, bgdModel, fgdModel, 1, GC_INIT_WITH_MASK);else// 否则用rect初始化grabCutgrabCut(*image, mask, rect, bgdModel, fgdModel, 1, GC_INIT_WITH_RECT);