虽然在开发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 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.