win7+VS2013+OpenCV 2.4.9
鼠标事件响应采用回调函数CallBack的方式来处理。
*******************************************************************************
其实回调就是一种利用函数指针进行函数调用的过程.
为什么要用回调呢?比如我要写一个子模块给你用, 来接收远程socket发来的命令.当我接收到命令后, 需要调用你的主模块的函数, 来进行相应的处理.但是我不知道你要用哪个函数来处理这个命令, 我也不知道你的主模块是什么.cpp或者.h, 或者说, 我根本不用关心你在主模块里怎么处理它, 也不应该关心用什么函数处理它...... 怎么办?
使用回调!
—— lone wolf
使用回调函数实际上就是在调用某个函数(通常是API函数)时,将自己的一个函数(这个函数为回调函数)的地址作为参数传递给那个函数。而那个函数在需要的时候,利用传递的地址调用回调函数,这时你可以利用这个机会在回调函数中处理消息或完成一定的操作。
—— 某专家
回调函数,就是由你自己写的。你需要调用另外一个函数,而这个函数的其中一个参数,就是你的这个回调函数名。这样,系统在必要的时候,就会调用你写的回调函数,这样你就可以在回调函数里完成你要做的事。
—— 绿叶
http://hi.baidu.com/zhuyipeng/blog/item/863fefdb7c736c63d1164eec.html 是一篇比较好的文章。
什么是回调函数?
回调函数是应用程序提供给Windows系统DLL或其它DLL调用的函数,一般用于截获消息、获取系统信息或处理异步事件。应用程序把回调函数的地址指针告诉DLL,而DLL在适当的时候会调用该函数。回调函数必须遵守事先规定好的参数格式和传递方式,否则DLL一调用它就会引起程序或系统的崩溃。通常情况下,回调函数采用标准WindowsAPI的调用方式,即__stdcall,当然,DLL编制者可以自己定义调用方式,但客户程序也必须遵守相同的规定。在__stdcall方式下,函数的参数按从右到左的顺序压入堆栈,除了明确指明是指针或引用外,参数都按值传递,函数返回之前自己负责把参数从堆栈中弹出。
理解回调函数!
—— jufengfeng
Function Pointers provide the concept of callback functions.
—— newty.de
*******************************************************************************
看了这么多的资料,我只将每位的定义总结一下就一句话:回调函数就是函数指针的一种用法。
在鼠标事件处理中,
回调函数callback可以是满足指定输入参数以及返回参数类型的任何函数。
这里,我们必须清楚告诉回调函数触发的事件以及触发位置。
函数还需要知道,用户是否在触发鼠标事件的同时触发了shift或alt等按键。
回调函数格式:
void CvMouseCallback(
int event,//鼠标事件
int x,//鼠标指针(cruser)当前位置的水平坐标
int y,//鼠标指针(cruser)当前位置的垂直坐标
int flags,//鼠标事件标志
void* param//额外需要传入的参数
);
还需要将回调函数注册到OpenCV中,实现的函数是cvSetMouseCallback
void cvSetMouseCallback(
const char* window_name,//指定回调函数需要注册到的窗口,也就是产生事件的窗口
CvMouseCallback on_mouse,//自己定义的回调函数
void* param=NULL//给CvMouseCallback传入的参数
);
例程:
实现一个程序,使得用户可以通过鼠标来画矩形。
//An example program in which the
//user can draw boxes on the screen.
#include <cv.h>
#include<highgui.h>
void my_mouse_callback(int event,int x,int y, int flags,void* param);//自定义的回调函数声明
CvRect box;
bool drawing_box=false;
void draw_box(IplImage* img,CvRect rect)
{
cvRectangle(
img,
cvPoint(box.x,box.y),
cvPoint(box.x+box.width,box.y+box.height),
//cvScalar(0xff,0x00,0x00);
CV_RGB(255,0,0)//red
);
}
int main(int argc,char** argv)
{
box=cvRect(-1,-1,0,0);
IplImage* image=cvCreateImage(
cvSize(512,512),
IPL_DEPTH_8U,
3);
cvZero(image);
IplImage* temp=cvCloneImage(image);//复制全部图片内容
cvNamedWindow("Box Example");
cvSetMouseCallback(//注册回调函数
"Box Example",
my_mouse_callback,
(void*)image);
while(1){
cvCopyImage(image,temp);
if(drawing_box) draw_box(temp,box);
cvShowImage("Box Example",temp);
if(cvWaitKey(15)==27)break;
}
cvReleaseImage(&image);
cvReleaseImage(&temp);
cvDestroyAllWindows();
}
void my_mouse_callback(int event,int x,int y, int flags,void* param)
{
IplImage* image=(IplImage*) param;
switch (event)
{
//鼠标拖动
//依照当前点坐标和矩形起始点坐标设置矩形的宽度和高度
case CV_EVENT_MOUSEMOVE:{
if(drawing_box)
{
box.width=x-box.x;
box.height=y-box.y;
}
}
break;
//鼠标左键按下,开始绘图,设置矩形起始点
case CV_EVENT_LBUTTONDOWN:{
drawing_box=true;
box=cvRect(x,y,0,0);
}
break;
//鼠标左键弹起,绘图结束,把矩形写入image
//如果width或height是负数,取其绝对值,并将靠近左上角的顶点设为矩形起始点
case CV_EVENT_LBUTTONUP:{
drawing_box=false;
if(box.width<0)
{
box.x+=box.width;
box.width*=-1;
}
if(box.height<0)
{
box.y+=box.height;
box.height*=-1;
}
draw_box(image,box);//将矩形写入image
}
break;
default:
break;
}
}
运行cmd,效果图: