轉自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 }