Opencv 手动选择ROI以及鼠标交互
转自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 }