虽然在开发APP中,鼠标操作开发非常简单,它也属于用户接口设计,一直使用Misscoft Studio 中C/C++或.net来做,但是如果只需要简单的鼠标,键盘操作。而OpenCV并未直接提供库的函数进行鼠标操作。本节介绍使用OpenCV完成鼠标操作相关知识。
鼠标操作主要理论有两点,第一是监控鼠标操作,鼠标点击,移动,松开,然后通过mouse_event识别判断出那一种鼠标的操作,根据不同的操作然后进行处理;第二是在主函数中加入鼠标的回调函数,将鼠标操作与程序的窗口绑定。每当滑动鼠标在窗口点击一下的时候,都会有固定三个动作,即点击动作(click),移动动作(Down)和松开动作(move)。因此,鼠标在点击时候执行onMouse函数。同时,还是有一个回调函数。如下所介绍:
OpenCV中,提供的鼠标回调函数是 setMouseCallback,函数声明如下:
//! assigns callback for mouse events CV_EXPORTS void setMouseCallback(const string& winname, MouseCallback onMouse, void* userdata = 0);函数参数
函数使用实例:
OpenCV1.X版
cvNamedWindow("src",1); cvSetMouseCallback( "src", on_mouse, 0 );OpenCV2.X版
namedWindow("img"); setMouseCallback("img",on_mouse,0);
OpenCV中,鼠标相应处理函数一般默认形参和返回参数,函数形式如下:
void on_mouse(int event,int x,int y,int flags,void *ustc)参数介绍:
enum { EVENT_MOUSEMOVE =0, //滑动 EVENT_LBUTTONDOWN =1, //左键点击 EVENT_RBUTTONDOWN =2, //右键点击 EVENT_MBUTTONDOWN =3, //中间点击 EVENT_LBUTTONUP =4, //左键释放 EVENT_RBUTTONUP =5, //右键释放 EVENT_MBUTTONUP =6, //中间释放 EVENT_LBUTTONDBLCLK =7, //左键双击 EVENT_RBUTTONDBLCLK =8, //右键双击 EVENT_MBUTTONDBLCLK =9 //中间释放 };
enum { EVENT_FLAG_LBUTTON =1, //左键拖拽 EVENT_FLAG_RBUTTON =2, //右键拖拽 EVENT_FLAG_MBUTTON =4, //中间拖拽 EVENT_FLAG_CTRLKEY =8, //(8~15)按Ctrl不放事件 EVENT_FLAG_SHIFTKEY =16, //(16~31)按Shift不放事件 EVENT_FLAG_ALTKEY =32 //(32~39)按Alt不放事件(后面8-39还有待研究) };
/* Initializes font structure used further in cvPutText */ CVAPI(void) cvInitFont( CvFont* font, int font_face, double hscale, double vscale, double shear CV_DEFAULT(0), int thickness CV_DEFAULT(1), int line_type CV_DEFAULT(8));
参数介绍:
/* basic font types */ #define CV_FONT_HERSHEY_SIMPLEX 0 //正常大小无衬线字体. #define CV_FONT_HERSHEY_PLAIN 1 //小号无衬线字体. #define CV_FONT_HERSHEY_DUPLEX 2 //正常大小无衬线字体(比CV_FONT_HERSHEY_SIMPLEX更复杂). #define CV_FONT_HERSHEY_COMPLEX 3 //正常大小有衬线字体. #define CV_FONT_HERSHEY_TRIPLEX 4 //正常大小有衬线字体(比CV_FONT_HERSHEY_COMPLEX更复杂). #define CV_FONT_HERSHEY_COMPLEX_SMALL 5 //CV_FONT_HERSHEY_COMPLEX的小译本 #define CV_FONT_HERSHEY_SCRIPT_SIMPLEX 6 //手写风格字体. #define CV_FONT_HERSHEY_SCRIPT_COMPLEX 7 //手写风格字体(比CV_FONT_HERSHEY_SCRIPT_SIMPLEX更复杂).此参数可以由一个值及可选择的CV_FONT_ITALIC(斜体字)字体标记合成。
说明:
函数cvInitFont完成对字体结构的初始化。注opencv对中文不支持。
/* Renders text stroke with specified font and color at specified location. CvFont should be initialized with cvInitFont */ CVAPI(void) cvPutText( CvArr* img, const char* text, CvPoint org, const CvFont* font, CvScalar color );
参数介绍:
说明:
函数cvPutText将具有指定字体和指定颜色的文本加入到图像中。加入到图像中的文本被感兴趣的矩形框圈定。
源始图片
int bound(short i,short a,short b) { return min(max(i,min(a,b)),max(a,b)); } CvScalar InverseColor(CvScalar c){ CvScalar s; for(int i=0;i<=2;++i) s.val[i]=255-c.val[i]; return s; } int n=0; vector<CvPoint> points; void on_mouse( int event, int x, int y, int flags, void* ustc){ CvPoint pt; CvPoint tmp_pt = {-1,-1}; CvFont font; cvInitFont(&font, CV_FONT_HERSHEY_SIMPLEX, 0.4, 0.4, 0, 1, CV_AA); char temp[16]; CvSize text_size; int baseline; CvScalar clrPoint=cvScalar(255,0,0,0); CvScalar clrText=cvScalar(0, 0, 255, 0); if( event == CV_EVENT_MOUSEMOVE ) { cvCopy(dst,src); x=bound(x,0,src->width-1); y=bound(y,0,src->height-1); pt = cvPoint(x,y); cvCircle( src, pt, 2,clrPoint ,CV_FILLED, CV_AA, 0 ); sprintf(temp,"%d (%d,%d)",n+1,x,y); cvGetTextSize(temp,&font,&text_size,&baseline); tmp_pt.x = bound(pt.x,0,src->width-text_size.width); tmp_pt.y = bound(pt.y,text_size.height+baseline,src->height-1-baseline); cvPutText(src,temp, tmp_pt, &font, clrText); cvShowImage( "src", src ); } else if( event == CV_EVENT_LBUTTONDOWN) { pt = cvPoint(x,y); points.push_back(pt); n++; cvCircle( src, pt, 2, clrPoint ,CV_FILLED, CV_AA, 0 ); sprintf(temp,"%d (%d,%d)",n,x,y); cvGetTextSize(temp,&font,&text_size,&baseline); tmp_pt.x = bound(pt.x,0,src->width-text_size.width); tmp_pt.y = bound(pt.y,text_size.height+baseline,src->height-1-baseline); cvPutText(src,temp, tmp_pt, &font, clrText); cvCopy(src,dst); cvShowImage( "src", src ); } else if( event == CV_EVENT_RBUTTONDOWN ) { if(!points.empty()) { cvCopy(dst,src); pt=points.back(); points.pop_back(); cvCircle( src, pt, 2, getInverseColor(clrPoint),CV_FILLED, CV_AA, 0 ); sprintf(temp,"%d (%d,%d)",n,pt.x,pt.y); --n; cvGetTextSize(temp,&font,&text_size,&baseline); tmp_pt.x = bound(pt.x,0,src->width-text_size.width); tmp_pt.y = bound(pt.y,text_size.height+baseline,src->height-1-baseline); cvPutText(src,temp, tmp_pt, &font, getInverseColor(clrText)); cvCopy(src,dst); cvShowImage( "src", src ); } } }输出结果
void on_mouse( int event, int x, int y, int flags, void* ustc){ static CvPoint pre_pt = {-1,-1}; static CvPoint cur_pt = {-1,-1}; CvFont font; cvInitFont(&font, CV_FONT_HERSHEY_SIMPLEX, 0.5, 0.5, 0, 1, CV_AA); char temp[16]; if( event == CV_EVENT_LBUTTONDOWN){ cvCopy(dst,src); sprintf(temp,"(%d,%d)",x,y); pre_pt = cvPoint(x,y); cvPutText(src,temp, pre_pt, &font, cvScalar(0,0, 0, 255)); cvCircle( src, pre_pt, 3,cvScalar(255,0,0,0) ,CV_FILLED, CV_AA, 0 ); cvShowImage( "src", src ); cvCopy(src,dst); } if( event == CV_EVENT_MOUSEMOVE && !(flags & CV_EVENT_FLAG_LBUTTON)){ cvCopy(dst,src); sprintf(temp,"(%d,%d)",x,y); cur_pt = cvPoint(x,y); cvPutText(src,temp, cur_pt, &font, cvScalar(0,0, 0, 255)); cvShowImage( "src", src ); } if( event == CV_EVENT_MOUSEMOVE && (flags & CV_EVENT_FLAG_LBUTTON)){ cvCopy(dst,src); sprintf(temp,"(%d,%d)",x,y); cur_pt = cvPoint(x,y); cvPutText(src,temp, cur_pt, &font, cvScalar(0,0, 0, 255)); cvRectangle(src, pre_pt, cur_pt, cvScalar(0,255,0,0), 1, 8, 0 ); cvShowImage( "src", src ); } if( event == CV_EVENT_LBUTTONUP){ sprintf(temp,"(%d,%d)",x,y); cur_pt = cvPoint(x,y); cvPutText(src,temp, cur_pt, &font, cvScalar(0,0, 0, 255)); cvCircle( src, cur_pt, 3,cvScalar(255,0,0,0) ,CV_FILLED, CV_AA, 0 ); cvRectangle( src, pre_pt, cur_pt, cvScalar(0,255,0,0), 1, 8, 0 ); cvShowImage( "src", src ); cvCopy(src,dst); } }输出结果
void on_mouse(int event,int x,int y,int flags,void *ustc){ static Point pre_pt = (-1,-1); static Point cur_pt = (-1,-1); char temp[16]; if (event == CV_EVENT_LBUTTONDOWN){ org.copyTo(img); sprintf(temp,"(%d,%d)",x,y); pre_pt = Point(x,y); putText(img,temp,pre_pt,FONT_HERSHEY_SIMPLEX,0.5,Scalar(0,0,0,255),1,8); circle(img,pre_pt,2,Scalar(255,0,0,0),CV_FILLED,CV_AA,0); imshow("img",img); } if (event == CV_EVENT_MOUSEMOVE && !(flags & CV_EVENT_FLAG_LBUTTON)){ img.copyTo(tmp); sprintf(temp,"(%d,%d)",x,y); cur_pt = Point(x,y); putText(tmp,temp,cur_pt,FONT_HERSHEY_SIMPLEX,0.5,Scalar(0,0,0,255)); imshow("img",tmp); } if (event == CV_EVENT_MOUSEMOVE && (flags & CV_EVENT_FLAG_LBUTTON)){ img.copyTo(tmp); sprintf(temp,"(%d,%d)",x,y); cur_pt = Point(x,y); putText(tmp,temp,cur_pt,FONT_HERSHEY_SIMPLEX,0.5,Scalar(0,0,0,255)); rectangle(tmp,pre_pt,cur_pt,Scalar(0,255,0,0),1,8,0); imshow("img",tmp); } if (event == CV_EVENT_LBUTTONUP){ org.copyTo(img); sprintf(temp,"(%d,%d)",x,y); cur_pt = Point(x,y); putText(img,temp,cur_pt,FONT_HERSHEY_SIMPLEX,0.5,Scalar(0,0,0,255)); circle(img,pre_pt,2,Scalar(255,0,0,0),CV_FILLED,CV_AA,0); rectangle(img,pre_pt,cur_pt,Scalar(0,255,0,0),1,8,0); imshow("img",img); img.copyTo(tmp); int width = abs(pre_pt.x - cur_pt.x); int height = abs(pre_pt.y - cur_pt.y); if (width == 0 || height == 0){ printf("width == 0 || height == 0"); return; } dst = org(Rect(min(cur_pt.x,pre_pt.x),min(cur_pt.y,pre_pt.y),width,height)); namedWindow("dst"); imshow("dst",dst); waitKey(0); } }输出结果
上图像中选择矩形区域的显示,如下图所示:
如下图中,Img Win显示图像并为选择感兴趣地矩形区域,而Dst Win为显示选择感兴趣地矩形区域的图像。即:
注:OpenCV1.x版中贴出参考代码变量src,dst分别为输入图像和输出图像变量并且申明全局变量和变量类型为Ipllmage,OpenCV2.X版中贴出参考代码变量org, dst,img,tmp都申明全局变量并且为cv::Mat类型。其中如何调用on_Mouse函数,请见回调函数setMouseCallback中函数使用实例。
关于Image Engineering & Computer Vision的更多讨论与交流,敬请关注本博和新浪微博songzi_tea.