学习OpenCV2——抠图及setmousecallback说明
2016-03-10 01:12
267 查看
用鼠标截取图像区域是一种常用操作,我参考了网上众多实现的方法,觉得以下方法最简洁。特此学习并分享。
下面程序实现了从视频中选择一个区域并单独显示。
winname:窗口的名字
onMouse:鼠标响应函数,回调函数。指定窗口里每次鼠标时间发生的时候,被调用的函数指针。 这个函数的原型应该为void onMouse(int event, int x, int y, int flags, void* param);
userdate:传给回调函数的参数
event:
#defineCV_EVENT_MOUSEMOVE 0 移动
#defineCV_EVENT_LBUTTONDOWN 1 左键按下
#defineCV_EVENT_RBUTTONDOWN 2 右键按下
#defineCV_EVENT_MBUTTONDOWN 3 中键按下
#defineCV_EVENT_LBUTTONUP 4 左键升起
#defineCV_EVENT_RBUTTONUP 5 右键升起
#defineCV_EVENT_MBUTTONUP 6 中键升起
#defineCV_EVENT_LBUTTONDBLCLK 7 左键双击
#defineCV_EVENT_RBUTTONDBLCLK 8 右键双击
#defineCV_EVENT_MBUTTONDBLCLK 9 中键双击
flag:
#defineCV_EVENT_FLAG_LBUTTON 1 左键拖曳
#defineCV_EVENT_FLAG_RBUTTON 2 右键拖曳
#defineCV_EVENT_FLAG_MBUTTON 4 中键拖曳
#defineCV_EVENT_FLAG_CTRLKEY 8 (8~15)按Ctrl不放事件
#defineCV_EVENT_FLAG_SHIFTKEY 16 (16~31)按Shift不放事件
#defineCV_EVENT_FLAG_ALTKEY 32 (32~39)按Alt不放事件
event是鼠标动作,flag是鼠标动作标志符,两者结合就能描述鼠标的各种动作了。如果要描述鼠标拖拽画线的操作,可以这样判断
event == CV_EVENT_MOUSEMOVE && (flags & CV_EVENT_FLAG_LBUTTON)
关于使用setmousecallback的几点注意事项
1)回调函数是void类型的,也就是说没有返回值,如果要把某变量的值传回main,有两种方法:1、把该变量定义为全局变量;2、把要回传的参数写成一个类,参照 http://blog.csdn.net/gxiaob/article/details/8213796
2)回调函数一旦运行就会监听鼠标动作,相当于开了一个和main并行的进程,该进程会随着main结束而结束。
这一点可以从上面的程序得到印证。同样是抠图,程序1用的是视频,程序2和程序3用的是图片,程序1的图片显示在main中实现,程序2和程序3是在回调函数中实现。如果我们想用图片,并且在main中显示结果,那么必须将原main程序的主体放在一个循环里。也就是让程序不停地显示图片,不停的监听。否则,虽然回调函数还在监听鼠标动作,但main已经运行到程序结尾处。尽管回调函数获得的参数是对的,但main里已经没有变量来接受这些参数了。
3)一般情况下,回调函数一旦运行就会一直运行,除非主函数结束。而这会占用系统资源,我们可以在获得想要的参数后中止回调函数运行。方法是destroyWindow( ),只要监听的窗口没了,回调函数也就结束了。
1.截取矩形区域
原文 http://www.cnblogs.com/tornadomeet/archive/2012/05/04/2483444.html下面程序实现了从视频中选择一个区域并单独显示。
程序1
//**************本程序练习了鼠标回调函数********************* #include <opencv2/highgui/highgui.hpp> #include <opencv2/core/core.hpp> #include <stdio.h> using namespace cv; using namespace std; ///--定义几个全局变量 Rect select; //鼠标选择的矩形框 bool mousedown_flag = false; //鼠标按下的标识符 bool select_flag = false; //选择区域的标识符 Point origin; Mat frame; void onMouse(int event,int x,int y,int,void*) { //注意onMouse是void类型的,没有返回值! //为了把这些变量的值传回主函数,这些变量必须设置为全局变量 if(mousedown_flag) { select.x=MIN(origin.x,x); //不一定要等鼠标弹起才计算矩形框,而应该在鼠标按下开始到弹起这段时间实时计算所选矩形框 select.y=MIN(origin.y,y); select.width=abs(x-origin.x); //算矩形宽度和高度 select.height=abs(y-origin.y); select&=Rect(0,0,frame.cols,frame.rows); //保证所选矩形框在视频显示区域之内 } if(event==CV_EVENT_LBUTTONDOWN) { mousedown_flag=true; select_flag = false; origin=Point(x,y); select=Rect(x,y,0,0); //这里一定要初始化,宽和高为(0,0)是因为在opencv中Rect矩形框类内的点是包含左上角那个点的,但是不含右下角那个点 } else if(event==CV_EVENT_LBUTTONUP) { mousedown_flag=false; select_flag = true; } } int main(int argc, unsigned char* argv[]) { //打开摄像头 VideoCapture cam(0); if (!cam.isOpened()) return -1; //建立窗口 namedWindow("camera",1); //捕捉鼠标 setMouseCallback("camera",onMouse,0); Mat frameROI; while(1) { //读取一帧图像 cam>>frame; if(frame.empty()) return -1; //显示选择的区域 if(select_flag) { frameROI = frame(select); //设置感兴趣区域ROI imshow("roi",frameROI); //显示ROI } //在每一帧图像上画矩形框 rectangle(frame,select,Scalar(255,0,0),1,8,0); //显示原视频帧 imshow("camera",frame); //显示所选区域的值 printf("\n X = %d, Y = %d, Width = %d, Height = %d",select.x, select.y, select.width, select.height); //键盘响应 int c = waitKey(20); if(27==c)//ESC键 return -1; } return 0; }
2. 截取任意区域
程序2:
// http://blog.csdn.net/cv_yuippe/article/details/13035063 #include <iostream> #include <opencv2/highgui/highgui.hpp> #include <opencv2/imgproc/imgproc.hpp> using namespace std; using namespace cv; Point prev_pt = Point(-1, -1); //如果将prev_pt写在on_mouse里,画的线会出问题,不知道怎么回事 Mat img,BG_mask,FG_mask; void on_mouse(int event, int x, int y, int flags, void* ) { if ( event == CV_EVENT_LBUTTONUP || !(flags & CV_EVENT_FLAG_LBUTTON)) //松开鼠标左键或者不是左拖拽 { prev_pt = Point(-1, -1); } else if (event == CV_EVENT_LBUTTONDOWN) //按下左键 { prev_pt = Point(x,y); } else if ( event == CV_EVENT_MOUSEMOVE && (flags & CV_EVENT_FLAG_LBUTTON)) //移动鼠标并且左拖拽 { Point pt = Point(x, y); if ( prev_pt.x < 0) { prev_pt = pt; } line(BG_mask, prev_pt, pt, Scalar(0),1,8,0); //背景模板上划线 line(FG_mask, prev_pt, pt, Scalar(255),1,8,0); //前景模板上划线 line(img, prev_pt, pt, Scalar::all(255),1,8,0); //原图上划线 prev_pt = pt; imshow("image", img); } if (event == CV_EVENT_RBUTTONUP) //右键UP,截取所选区域 { Mat BG,FG; //若希望将圈选的结果相加,定义在外头 floodFill(BG_mask,Point(x,y),Scalar(0)); //point种子点所在的连通域被填充 floodFill(FG_mask,Point(x,y),Scalar(255)); imshow("BG_mask",BG_mask); //显示模板 imshow("FG_mask",FG_mask); img.copyTo(BG,BG_mask);//mask中所有不为零的点被dst对应的值填充 img.copyTo(FG,FG_mask);//mask中所有不为零的点被dst对应的值填充 imshow("FG",FG); imshow("BG",BG); img.copyTo(img); BG_mask.setTo(Scalar(255)); //重新取值 FG_mask.setTo(Scalar(0)); //重新取值 } } int main( ) { Mat image = imread("D:/Programs_L/OpenCV249/sources/samples/cpp/tutorial_code/images/cat.jpg"); if(!image.data) { cout<<"Can not open the image!"<<endl; system("pause"); } //初始化前景和背景模板 FG_mask = Mat(image.size(),CV_8UC1,Scalar(0)); //纯白 BG_mask = Mat(image.size(),CV_8UC1,Scalar(255)); //纯黑 image.copyTo(img); imshow("image",img); //显示原图 //鼠标回调函数 cvSetMouseCallback("image",on_mouse,0); waitKey(0); return 0; }结果如下
程序3:
//http://blog.csdn.net/gxiaob/article/details/8330579 //截取任意形状 #include "cv.h" #include "highgui.h" #include "cxcore.h" #include <iostream> using namespace std; using namespace cv; void MouseDraw(int event,int x,int y,int flags,void*param); struct MouseArgs{ IplImage* img; CvPoint p_start; CvPoint p_end; CvSeq* seq; CvMemStorage* storage; int points; // init MouseArgs():img(0),points(0){ p_start = cvPoint(-1,-1); p_end = cvPoint(-1,-1); storage = cvCreateMemStorage(0); seq = cvCreateSeq( CV_32SC2,sizeof(CvSeq),sizeof(CvPoint), storage ); } // destroy void Destroy(){ if(!img) cvReleaseImage(&img); cvReleaseMemStorage(&storage ); seq = NULL; img = NULL; } }; int main( int argc,char** argv ) { // loading image char* imf = "D:/Programs_L/OpenCV249/sources/samples/cpp/tutorial_code/images/cat.jpg"; IplImage* pImg_org = cvLoadImage(imf,1); if(!pImg_org){ cout<<"cann't load image!"<<endl; return-1; } // 回调参数 MouseArgs* m_arg =new MouseArgs(); m_arg->img = cvCloneImage(pImg_org); // 画图窗口 cvNamedWindow("Draw ROI",CV_WINDOW_AUTOSIZE); // 设置鼠标事件的回调函数 cvSetMouseCallback("Draw ROI",MouseDraw,(void*)m_arg); // 拖动鼠标作画 while(1) { cvShowImage("Draw ROI",m_arg->img); // 按 esc 键退出绘图模式,获得矩形 if(cvWaitKey(100)==27) break; } // 输出 if(m_arg->points < 1) return 0; cout<<m_arg->points <<endl; // 获得掩模 IplImage* mask = cvCreateImage( cvGetSize(pImg_org), 8, 1 ); cvZero(mask); CvPoint* PointArr =new CvPoint[m_arg->points]; cvCvtSeqToArray(m_arg->seq, PointArr); cvFillConvexPoly(mask,PointArr,m_arg->points,cvScalarAll(255),CV_AA,0); delete[] PointArr; cvNamedWindow("Mask",CV_WINDOW_AUTOSIZE); cvShowImage("Mask",mask); // 获得区域 IplImage* roi = cvCreateImage( cvGetSize(pImg_org), 8, 3 ); cvCopy(pImg_org,roi,mask); cvNamedWindow("ROI",CV_WINDOW_AUTOSIZE); cvShowImage("ROI",roi); // cvWaitKey(0); cvDestroyWindow("Draw ROI"); cvDestroyWindow("Mask"); cvDestroyWindow("ROI"); // m_arg->Destroy (); delete m_arg; cvReleaseImage(&pImg_org); cvReleaseImage(&mask); cvReleaseImage(&roi); // getchar(); return 0; } // 描点式 /* void MouseDraw(int event,int x,int y,int flags,void*param) { MouseArgs* m_arg = (MouseArgs*) param; if( !m_arg->img ) return; if( event == CV_EVENT_LBUTTONUP || !(flags & CV_EVENT_FLAG_LBUTTON) ) { m_arg->p_end = m_arg->p_start; } else if( event == CV_EVENT_LBUTTONDOWN ) { m_arg->p_start = cvPoint(x,y); cvSeqPush( m_arg->seq, &m_arg->p_start); // 描点记录 m_arg->points += 1; if(m_arg->p_start.x>0 && m_arg->p_end.x>0){ cvLine( m_arg->img, m_arg->p_start, m_arg->p_end, cvScalar(0,0,255) ); cvLine( m_arg->img, m_arg->p_start, m_arg->p_start, cvScalar(128,0,255) ); } } } */ // 拖动式 void MouseDraw(int event,int x,int y,int flags,void*param) { MouseArgs* m_arg = (MouseArgs*) param; if( !m_arg->img ) return; if( event == CV_EVENT_LBUTTONUP || !(flags & CV_EVENT_FLAG_LBUTTON) ) { m_arg->p_start = cvPoint(x,y); } else if( event == CV_EVENT_LBUTTONDOWN ) { m_arg->p_start = cvPoint(x,y); cvSeqPush( m_arg->seq, &m_arg->p_start); m_arg->points += 1; if(m_arg->p_start.x>0 && m_arg->p_end.x>0){ cvLine( m_arg->img, m_arg->p_start, m_arg->p_start, cvScalar(128,0,255) ); } } else if( event == CV_EVENT_MOUSEMOVE && (flags & CV_EVENT_FLAG_LBUTTON) ) { CvPoint pt = cvPoint(x,y); if( m_arg->p_start.x > 0 ){ cvLine( m_arg->img, m_arg->p_start, pt, cvScalar(128,0,255) ); m_arg->p_start = pt; cvSeqPush( m_arg->seq, &m_arg->p_start); m_arg->points += 1; } } }程序3能用描点法截取区域。
3. setmousecallback的说明
void setMousecallback(const string& winname, MouseCallback onMouse, void* userdata=0)winname:窗口的名字
onMouse:鼠标响应函数,回调函数。指定窗口里每次鼠标时间发生的时候,被调用的函数指针。 这个函数的原型应该为void onMouse(int event, int x, int y, int flags, void* param);
userdate:传给回调函数的参数
event:
#defineCV_EVENT_MOUSEMOVE 0 移动
#defineCV_EVENT_LBUTTONDOWN 1 左键按下
#defineCV_EVENT_RBUTTONDOWN 2 右键按下
#defineCV_EVENT_MBUTTONDOWN 3 中键按下
#defineCV_EVENT_LBUTTONUP 4 左键升起
#defineCV_EVENT_RBUTTONUP 5 右键升起
#defineCV_EVENT_MBUTTONUP 6 中键升起
#defineCV_EVENT_LBUTTONDBLCLK 7 左键双击
#defineCV_EVENT_RBUTTONDBLCLK 8 右键双击
#defineCV_EVENT_MBUTTONDBLCLK 9 中键双击
flag:
#defineCV_EVENT_FLAG_LBUTTON 1 左键拖曳
#defineCV_EVENT_FLAG_RBUTTON 2 右键拖曳
#defineCV_EVENT_FLAG_MBUTTON 4 中键拖曳
#defineCV_EVENT_FLAG_CTRLKEY 8 (8~15)按Ctrl不放事件
#defineCV_EVENT_FLAG_SHIFTKEY 16 (16~31)按Shift不放事件
#defineCV_EVENT_FLAG_ALTKEY 32 (32~39)按Alt不放事件
event是鼠标动作,flag是鼠标动作标志符,两者结合就能描述鼠标的各种动作了。如果要描述鼠标拖拽画线的操作,可以这样判断
event == CV_EVENT_MOUSEMOVE && (flags & CV_EVENT_FLAG_LBUTTON)
关于使用setmousecallback的几点注意事项
1)回调函数是void类型的,也就是说没有返回值,如果要把某变量的值传回main,有两种方法:1、把该变量定义为全局变量;2、把要回传的参数写成一个类,参照 http://blog.csdn.net/gxiaob/article/details/8213796
2)回调函数一旦运行就会监听鼠标动作,相当于开了一个和main并行的进程,该进程会随着main结束而结束。
这一点可以从上面的程序得到印证。同样是抠图,程序1用的是视频,程序2和程序3用的是图片,程序1的图片显示在main中实现,程序2和程序3是在回调函数中实现。如果我们想用图片,并且在main中显示结果,那么必须将原main程序的主体放在一个循环里。也就是让程序不停地显示图片,不停的监听。否则,虽然回调函数还在监听鼠标动作,但main已经运行到程序结尾处。尽管回调函数获得的参数是对的,但main里已经没有变量来接受这些参数了。
3)一般情况下,回调函数一旦运行就会一直运行,除非主函数结束。而这会占用系统资源,我们可以在获得想要的参数后中止回调函数运行。方法是destroyWindow( ),只要监听的窗口没了,回调函数也就结束了。
相关文章推荐
- PHP GD 图像处理组件的常用函数总结
- PHP图像处理之imagecreate、imagedestroy函数介绍
- jsvascript图像处理―(计算机视觉应用)图像金字塔
- Javascript图像处理思路及实现代码
- PHP图像处理之使用imagecolorallocate()函数设置颜色例子
- java数字图像处理基础使用imageio写图像文件示例
- 使用Java进行图像处理的一些基础操作
- javascript图像处理―边缘梯度计算函数
- Javascript图像处理―阈值函数实例应用
- Javascript图像处理―虚拟边缘介绍及使用方法
- PHP图像处理类库及演示分享
- php图像处理函数大全(推荐收藏)
- Javascript图像处理―图像形态学(膨胀与腐蚀)
- Javascript图像处理―平滑处理实现原理
- Swift图像处理之优化照片
- 在Ubuntu上安装OpenCV3.0和Python-openCV的经历
- VTK学习笔记之图像处理
- vtk 图像处理 多种 操作
- 05-VTK在图像处理中的应用(2)
- 新手上路之图像处理学习心得