转自http://www.cnblogs.com/tornadomeet/archive/2012/05/04/2483444.html
一些图像处理算法要求用户的参与,比如分割算法GrabCut需要用户选定初始区域或前/背景掩模,在用OpenCV实现里,就涉及到利用鼠标在图片上选定这些区域,这里讲讲常见的几种鼠标绘图:
1、绘制矩形并获得矩形区域图像:在显示图片的窗口,通过拖动鼠标绘制矩形,按ESC键退出绘图模式。
涉及函数:
void
cvSetMouseCallback(
const
char
* window_name, CvMouseCallback on_mouse,
void
* param=NULL );
--window_name
窗口的名字。
--on_mouse
指定窗口里每次鼠标事件发生的时候,被调用的函数指针。
这个函数的原型应该为
--
void
Foo(
int
event,
int
x,
int
y,
int
flags,
void
* param);
其中event是 CV_EVENT_*变量之一,
x和y是鼠标指针在图像坐标系的坐标(不是窗口坐标系),
flags是CV_EVENT_FLAG的组合(即上面的一些有关现在动作状态的预定义,现在鼠标没任何操作时为0),
param是用户定义的传递到cvSetMouseCallback函数调用 的参数。
--param
用户定义的传递到回调函数的参数。
函数 cvSetMouseCallback设定指定窗口鼠标事件发生时的回调函数。
详细使用方法,请参考opencv/samples/c/ffilldemo.c demo。

1 #include "cv.h"
2 #include "highgui.h"
3
4 #include <iostream>
5
6 using namespace std; 7 using namespace cv; 8
9 void DrawRect(IplImage*img,CvRect rect); 10 void MouseDraw(int event,int x,int y,int flags,void*param); 11
12 struct MouseArgs{ 13 IplImage* img; 14 CvRect box; 15 bool Drawing; 16 // init
17 MouseArgs():Drawing(false),img(0){ 18 box=cvRect(0,0,-1,-1); 19 } 20 // destroy
21 void Destroy(){ 22 if(!img) 23 cvReleaseImage(&img); 24 } 25 }; 26
27 int main(int argc, char** argv) 28 { 29 // loading image
30 char* imf = argc >= 2 ? argv[1] :"audi-2009.jpg"; 31
32 IplImage* pImg_org = cvLoadImage(imf,1); 33 if(!pImg_org){ 34 cout<<"cann't load image!"<<endl; 35 return -1; 36 } 37
38 // 回调参数
39 MouseArgs* m_arg = new MouseArgs(); 40 m_arg->img = cvCloneImage(pImg_org); 41
42 // 画图窗口
43 cvNamedWindow("Draw ROI",CV_WINDOW_AUTOSIZE); 44
45 // 设置鼠标事件的回调函数
46 cvSetMouseCallback("Draw ROI", 47 MouseDraw, 48 (void*)m_arg); 49
50 // 拖动鼠标作画
51 IplImage* temp=cvCloneImage(pImg_org); 52 while(1) 53 { 54 cvCopyImage(m_arg->img,temp); 55 if(m_arg->Drawing) 56 DrawRect(temp,m_arg->box); 57 cvShowImage("Draw ROI",temp); 58 // 按 esc 键退出绘图模式,获得矩形
59 if(cvWaitKey(100)==27) 60 break; 61
62 } 63 cvReleaseImage( &temp ); 64
65 // 获得ROI区域的图像
66 IplImage* roi; 67 if(m_arg->box.width<10 || m_arg->box.height<10) 68 { 69 roi=cvCloneImage(pImg_org); 70 } 71 else
72 { 73 roi=cvCreateImage(cvSize(m_arg->box.width,m_arg->box.height), 74 pImg_org->depth, 75 pImg_org->nChannels); 76 cvSetImageROI(pImg_org,m_arg->box);//设定ROI
77 roi=cvCloneImage(pImg_org);//复制出ROI区域的图像
78 cvResetImageROI(pImg_org); 79
80 } 81
82 cvNamedWindow( "ROI", 1 ); 83 cvShowImage( "ROI", roi ); 84
85 // 86 cvWaitKey(0); 87 cvDestroyWindow("Draw ROI"); 88
89 m_arg->Destroy (); 90 delete m_arg; 91 cvReleaseImage(&pImg_org); 92 cvReleaseImage(&roi); 93 // 94 return 0; 95
96 } 97
98 /*
99 描述:在图像上绘制矩形 100 */
101 void DrawRect(IplImage*img,CvRect rect) 102 { 103 cvRectangle(img, 104 cvPoint(rect.x,rect.y), 105 cvPoint(rect.x+rect.width,rect.y+rect.height), 106 cvScalar(255,0,0),3); 107 } 108
109 /*
110 描述:鼠标事件的回调函数 111 函数原型: void Foo(int event, int x, int y, int flags, void* param); 112 参数: event -- CV_EVENT_*变量之一, 113 x,y -- 鼠标指针在图像坐标系的坐标(不是窗口坐标系) 114 flags -- CV_EVENT_FLAG的组合 115 param -- 用户定义的传递到cvSetMouseCallback函数调用的参数 116 */
117 void MouseDraw(int event,int x,int y,int flags,void*param) 118 { 119 MouseArgs* m_arg = (MouseArgs*) param; 120 if(!m_arg->img) 121 return; 122
123 switch(event) 124 { 125 case CV_EVENT_MOUSEMOVE: // 鼠标移动时
126 { 127 if(m_arg->Drawing) 128 { 129 m_arg->box.width = x-m_arg->box.x; 130 m_arg->box.height = y-m_arg->box.y; 131 } 132 } 133 break; 134 case CV_EVENT_LBUTTONDOWN: // 左键按下
135 { 136 m_arg->Drawing = true; 137 m_arg->box = cvRect(x,y,0,0); 138 } 139 break; 140 case CV_EVENT_LBUTTONUP: // 左键弹起
141 { 142 m_arg->Drawing = false; 143 if (m_arg->box.width<0) 144 { 145 m_arg->box.x += m_arg->box.width; 146 m_arg->box.width *= -1; 147 } 148 if (m_arg->box.height<0) 149 { 150 m_arg->box.y += m_arg->box.height; 151 m_arg->box.height *= -1; 152 } 153 DrawRect(m_arg->img, m_arg->box); 154 } 155 break; 156 } 157 }
2、绘制任意形状并获得区域图像:原理同上,使用CvSeq记录轨迹点,然后用cvFillConvexPoly填充多边形区域形成掩模,最后用cvCopy拷贝区域图像。支持两种绘图模式,描点式(如PS之钢笔)和拖动式:

1 // mouse.cpp : 定义控制台应用程序的入口点。 2 // 3 4 #include "stdafx.h" 5 6 7 #include "cv.h" 8 #include "highgui.h" 9 #include "cxcore.h" 10 11 #include <iostream> 12 13 using namespace std; 14 using namespace cv; 15 void MouseDraw(int event,int x,int y,int flags,void*param); 16 struct MouseArgs{ 17 IplImage* img; 18 CvPoint p_start; 19 CvPoint p_end; 20 CvSeq* seq; 21 CvMemStorage* storage; 22 int points; 23 // init 24 MouseArgs():img(0),points(0){ 25 p_start = cvPoint(-1,-1); 26 p_end = cvPoint(-1,-1); 27 storage = cvCreateMemStorage(0); 28 seq = cvCreateSeq( CV_32SC2,sizeof(CvSeq),sizeof(CvPoint), storage ); 29 } 30 // destroy 31 void Destroy(){ 32 if(!img) 33 cvReleaseImage(&img); 34 cvReleaseMemStorage(&storage ); 35 seq = NULL; 36 img = NULL; 37 } 38 }; 39 40 int main( int argc,char** argv ) 41 { 42 // loading image 43 char* imf = argc >= 2 ? argv[1] :"5.jpg"; 44 45 IplImage* pImg_org = cvLoadImage(imf,1); 46 if(!pImg_org){ 47 cout<<"cann't load image!"<<endl; 48 return-1; 49 } 50 51 // 回调参数 52 MouseArgs* m_arg =new MouseArgs(); 53 m_arg->img = cvCloneImage(pImg_org); 54 55 // 画图窗口 56 cvNamedWindow("Draw ROI",CV_WINDOW_AUTOSIZE); 57 58 // 设置鼠标事件的回调函数 59 cvSetMouseCallback("Draw ROI",MouseDraw,(void*)m_arg); 60 61 // 拖动鼠标作画 62 while(1) 63 { 64 cvShowImage("Draw ROI",m_arg->img); 65 // 按 esc 键退出绘图模式,获得矩形 66 if(cvWaitKey(100)==27) 67 break; 68 69 } 70 71 // 输出 72 if(m_arg->points < 1) 73 return 0; 74 cout<<m_arg->points <<endl; 75 76 // 获得掩模 77 IplImage* mask = cvCreateImage( cvGetSize(pImg_org), 8, 1 ); 78 cvZero(mask); 79 80 CvPoint* PointArr =new CvPoint[m_arg->points]; 81 cvCvtSeqToArray(m_arg->seq, PointArr); 82 cvFillConvexPoly(mask,PointArr,m_arg->points,cvScalarAll(255),CV_AA,0); 83 delete[] PointArr; 84 cvNamedWindow("Mask",CV_WINDOW_AUTOSIZE); 85 cvShowImage("Mask",mask); 86 87 // 获得区域 88 IplImage* roi = cvCreateImage( cvGetSize(pImg_org), 8, 3 ); 89 cvCopy(pImg_org,roi,mask); 90 cvNamedWindow("ROI",CV_WINDOW_AUTOSIZE); 91 cvShowImage("ROI",roi); 92 93 // 94 cvWaitKey(0); 95 cvDestroyWindow("Draw ROI"); 96 cvDestroyWindow("Mask"); 97 cvDestroyWindow("ROI"); 98 99 // 100 m_arg->Destroy (); 101 delete m_arg; 102 cvReleaseImage(&pImg_org); 103 cvReleaseImage(&mask); 104 cvReleaseImage(&roi); 105 // 106 getchar(); 107 return 0; 108 } 109 // 描点式 110 /* 111 void MouseDraw(int event,int x,int y,int flags,void*param) 112 { 113 MouseArgs* m_arg = (MouseArgs*) param; 114 if( !m_arg->img ) 115 return; 116 117 if( event == CV_EVENT_LBUTTONUP || !(flags & CV_EVENT_FLAG_LBUTTON) ) 118 { 119 m_arg->p_end = m_arg->p_start; 120 } 121 else if( event == CV_EVENT_LBUTTONDOWN ) 122 { 123 m_arg->p_start = cvPoint(x,y); 124 cvSeqPush( m_arg->seq, &m_arg->p_start); // 描点记录 125 m_arg->points += 1; 126 if(m_arg->p_start.x>0 && m_arg->p_end.x>0){ 127 cvLine( m_arg->img, m_arg->p_start, m_arg->p_end, cvScalar(0,0,255) ); 128 cvLine( m_arg->img, m_arg->p_start, m_arg->p_start, cvScalar(128,0,255) ); 129 } 130 } 131 132 } 133 */ 134 // 拖动式 135 void MouseDraw(int event,int x,int y,int flags,void*param) 136 { 137 MouseArgs* m_arg = (MouseArgs*) param; 138 if( !m_arg->img ) 139 return; 140 141 if( event == CV_EVENT_LBUTTONUP || !(flags & CV_EVENT_FLAG_LBUTTON) ) 142 { 143 m_arg->p_start = cvPoint(x,y); 144 } 145 else if( event == CV_EVENT_LBUTTONDOWN ) 146 { 147 m_arg->p_start = cvPoint(x,y); 148 cvSeqPush( m_arg->seq, &m_arg->p_start); 149 m_arg->points += 1; 150 if(m_arg->p_start.x>0 && m_arg->p_end.x>0){ 151 cvLine( m_arg->img, m_arg->p_start, m_arg->p_start, cvScalar(128,0,255) ); 152 } 153 } 154 else if( event == CV_EVENT_MOUSEMOVE && (flags & CV_EVENT_FLAG_LBUTTON) ) 155 { 156 CvPoint pt = cvPoint(x,y); 157 if( m_arg->p_start.x > 0 ){ 158 cvLine( m_arg->img, m_arg->p_start, pt, cvScalar(128,0,255) ); 159 m_arg->p_start = pt; 160 cvSeqPush( m_arg->seq, &m_arg->p_start); 161 m_arg->points += 1; 162 } 163 164 } 165 166 }
3、鼠标控制动态缩放图像显示:在cvNamedWindow图像窗口中通过“ ALT和鼠标左键开始按下的时候放大图像”和“ALT和鼠标右键开始按下的时候缩小图像”:

1 #include "cv.h"
2 #include "highgui.h"
3
4 #include <iostream>
5
6 usingnamespace std; 7 usingnamespace cv; 8
9 structMouseArgs{ 10 IplImage* img_src; 11 IplImage* img_dst; 12 doublescale; 13 // init
14 MouseArgs():img_src(0),img_dst(0),scale(1.0){ 15 } 16 // destroy
17 voidDestroy(){ 18 if(!img_src) 19 cvReleaseImage(&img_src); 20 if(!img_dst) 21 cvReleaseImage(&img_dst); 22 } 23 }; 24
25 voidMouseResize(intevent,int x,int y,intflags,void*param); 26 IplImage* resize_img(IplImage* src,doubleimgzoom_scale); 27
28 int main( int argc,char** argv ) 29 { 30 // loading image
31 char* imf = argc >= 2 ? argv[1] :"audi-2009.jpg"; 32
33 IplImage* pImg_org = cvLoadImage(imf,1); 34 if(!pImg_org){ 35 cout<<"cann't load image!"<<endl; 36 return-1; 37 } 38
39 // 回调参数
40 MouseArgs* m_arg =new MouseArgs(); 41 m_arg->img_src = cvCloneImage(pImg_org); 42
43 // 画图窗口
44 cvNamedWindow("Resize",CV_WINDOW_AUTOSIZE); 45
46 // 设置鼠标事件的回调函数
47 cvSetMouseCallback("Resize", 48 MouseResize, 49 (void*)m_arg); 50
51 // 52 while(1) 53 { 54 cvShowImage("Resize",m_arg->img_src); 55 // 按 esc 键退出
56 if(cvWaitKey(100)==27) 57 break; 58
59 } 60
61 // 62 cvWaitKey(0); 63 cvDestroyWindow("Resize"); 64
65 // 66 getchar(); 67 return0; 68 } 69
70 voidMouseResize( intevent, int x, int y, int flags, void* param ) 71 { 72 MouseArgs* m_arg = (MouseArgs*) param; 73 if( !m_arg->img_src ) 74 return; 75
76 if( (event==CV_EVENT_LBUTTONUP) && (flags==CV_EVENT_FLAG_CTRLKEY) ) 77 { 78
79 } 80
81 // ALT和鼠标左键开始按下的时候放大图像
82 if( (event==CV_EVENT_LBUTTONUP) && (flags==CV_EVENT_FLAG_ALTKEY) ) 83 { 84
85 if(m_arg->scale<1.5) 86 { 87 m_arg->scale=1.1*m_arg->scale; 88 } 89 else
90 m_arg->scale=1.0; 91
92 // 放大图像
93 m_arg->img_dst = resize_img(m_arg->img_src, m_arg->scale); 94 m_arg->img_src = cvCloneImage(m_arg->img_dst); 95
96 } 97
98 //ALT和鼠标右键开始按下的时候缩小图像
99 if( (event==CV_EVENT_RBUTTONUP) && (flags==CV_EVENT_FLAG_ALTKEY) ) 100 { 101
102 if(m_arg->scale>0.0) 103 { 104 m_arg->scale=0.9*m_arg->scale; 105 } 106 else
107 m_arg->scale=0.5; 108
109 // 缩小图像
110 m_arg->img_dst = resize_img(m_arg->img_src, m_arg->scale); 111 m_arg->img_src = cvCloneImage(m_arg->img_dst); 112
113 } 114
115 } 116
117 IplImage* resize_img(IplImage* src,doubleimgzoom_scale) 118 { 119 IplImage* dst = cvCreateImage(cvSize( (int)(src->width*imgzoom_scale), (int)(src->height*imgzoom_scale) ), 120 src->depth, 121 src->nChannels); 122
123 cvResize(src, dst, CV_INTER_AREA ); 124
125 returndst; 126 }