OpenCV鼠标交互

本文转自http://blog.youtueye.com/skill/opencv-drawing.html

一些图像处理算法要求用户的参与,比如分割算法GrabCut需要用户选定初始区域或前/背景掩模,在用OpenCV实现里,就涉及到利用鼠标在图片上选定这些区域,这里讲讲常见的几种鼠标绘图:

1、绘制矩形并获得矩形区域图像:在显示图片的窗口,通过拖动鼠标绘制矩形,按ESC键退出绘图模式。

涉及函数:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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。

简单实现:

#include "cv.h"
#include "highgui.h"

#include <iostream>

using namespace std;
using namespace cv;

void DrawRect(IplImage*img,CvRect rect);
void MouseDraw(int event,int x,int y,int flags,void*param);

struct MouseArgs{
	IplImage* img;
    CvRect box;
	bool Drawing;
	// init
	MouseArgs():Drawing(false),img(0){
		box=cvRect(0,0,-1,-1);
	}
	// destroy
	void Destroy(){
		if(!img)
			cvReleaseImage(&img);
	}
};

int main(int argc, char** argv)
{
	// loading image
	char* imf = argc >= 2 ? argv[1] :"audi-2009.jpg";

	IplImage* pImg_org = cvLoadImage(imf,1);
	if(!pImg_org){
		cout<<"cann't load image!"<<endl;
		return -1;
	}

    // 回调参数
    MouseArgs* m_arg = new MouseArgs();
	m_arg->img = cvCloneImage(pImg_org);

	// 画图窗口
	cvNamedWindow("Draw ROI",CV_WINDOW_AUTOSIZE);

	// 设置鼠标事件的回调函数
	cvSetMouseCallback("Draw ROI",
		MouseDraw,
		(void*)m_arg); 

	// 拖动鼠标作画
	IplImage* temp=cvCloneImage(pImg_org);
	while(1)
	{
		cvCopyImage(m_arg->img,temp);
		if(m_arg->Drawing)
			DrawRect(temp,m_arg->box);
		cvShowImage("Draw ROI",temp);
        // 按 esc 键退出绘图模式,获得矩形
		if(cvWaitKey(100)==27)
			break;

	}
	cvReleaseImage( &temp );  

	// 获得ROI区域的图像
	IplImage*  roi;
	if(m_arg->box.width<10 || m_arg->box.height<10)
	{
		roi=cvCloneImage(pImg_org);
	}
	else
	{
		roi=cvCreateImage(cvSize(m_arg->box.width,m_arg->box.height),
			pImg_org->depth,
			pImg_org->nChannels);
		cvSetImageROI(pImg_org,m_arg->box);//设定ROI
		roi=cvCloneImage(pImg_org);//复制出ROI区域的图像
		cvResetImageROI(pImg_org);

	}

	cvNamedWindow( "ROI", 1 );
	cvShowImage( "ROI", roi );

	//
	cvWaitKey(0);
    cvDestroyWindow("Draw ROI");

	m_arg->Destroy ();
	delete m_arg;
	cvReleaseImage(&pImg_org);
	cvReleaseImage(&roi);
	//
	return 0;

}

/*
描述:在图像上绘制矩形
*/
void DrawRect(IplImage*img,CvRect rect)
{
	cvRectangle(img,
		cvPoint(rect.x,rect.y),
		cvPoint(rect.x+rect.width,rect.y+rect.height),
		cvScalar(255,0,0),3);
}

/*
描述:鼠标事件的回调函数
函数原型:  void Foo(int event, int x, int y, int flags, void* param);
参数: event -- CV_EVENT_*变量之一,
       x,y   -- 鼠标指针在图像坐标系的坐标(不是窗口坐标系)
       flags -- CV_EVENT_FLAG的组合
       param -- 用户定义的传递到cvSetMouseCallback函数调用的参数
*/
void MouseDraw(int event,int x,int y,int flags,void*param)
{
	MouseArgs* m_arg = (MouseArgs*) param;
    if(!m_arg->img)
		return;

	switch(event)
	{
	case CV_EVENT_MOUSEMOVE: // 鼠标移动时
		{
			if(m_arg->Drawing)
			{
				m_arg->box.width = x-m_arg->box.x;
				m_arg->box.height = y-m_arg->box.y;
			}
		}
		break;
	case CV_EVENT_LBUTTONDOWN: // 左键按下
		{
			m_arg->Drawing = true;
			m_arg->box = cvRect(x,y,0,0);
		}
		break;
	case CV_EVENT_LBUTTONUP: // 左键弹起
		{
			m_arg->Drawing = false;
			if (m_arg->box.width<0)
			{
				m_arg->box.x += m_arg->box.width;
				m_arg->box.width *= -1;
			}
			if (m_arg->box.height<0)
			{
				m_arg->box.y += m_arg->box.height;
				m_arg->box.height *= -1;
			}
			DrawRect(m_arg->img, m_arg->box);
		}
		break;
	}
}

2、绘制任意形状并获得区域图像:原理同上,使用CvSeq记录轨迹点,然后用cvFillConvexPoly填充多边形区域形成掩模,最后用cvCopy拷贝区域图像。支持两种绘图模式,描点式(如PS之钢笔)和拖动式:

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

 

3、鼠标控制动态缩放图像显示:在cvNamedWindow图像窗口中通过“ ALT和鼠标左键开始按下的时候放大图像”和“ALT和鼠标右键开始按下的时候缩小图像”:

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

你可能感兴趣的:(OpenCV鼠标交互)