【OpenCV】选择ROI区域
问题描述:在测试目标跟踪算法时,需要选择不同区域作为目标,进行目标跟踪,测试目标跟踪的效果。
解决思路:
1.OpenCV中提供了鼠标交互控制,利用setMouseCallback()给固定的窗口设置鼠标回调函数。
2.在鼠标回调函数中,选择感兴趣区域。
代码实现如下,将感兴趣区域封装在MouseSelect类中,提供选择点和矩形框两种模式。
1 #pragma once 2 #ifndef __MOUSESELECT_H__ 3 #define __MOUSESELECT_H__ 4 5 #include <opencv2/opencv.hpp> 6 #include <iostream> 7 8 #define MAX_OBJECTS 10 9 using namespace cv; 10 using namespace std; 11 typedef struct MouseSelectParams 12 { 13 vector<Point> pts; //Points of selected 14 char *win_name; 15 Mat *image; 16 unsigned int mode; //1 Point,2 Rect 17 int n; // selected object number 18 }MouseSelectParams; 19 20 void on_mouse(int event,int x,int y,int flags,void *param); 21 class MouseSelect 22 { 23 public: 24 MouseSelect(); 25 ~MouseSelect(); 26 27 void select_rect(Mat &frame); 28 void select_point(Mat &frame); 29 30 vector<Point> vPoints; 31 vector<Rect> vRects; 32 unsigned int obj_selected; 33 private: 34 int get_rects(Mat &frame); 35 int get_points(Mat &frame); 36 37 }; 38 #endif
1 #include "MouseSelect.h" 2 3 MouseSelectParams *g_mousep; 4 MouseSelect::MouseSelect() {} 5 6 MouseSelect::~MouseSelect() {} 7 8 void MouseSelect::select_rect(Mat &frame) 9 { 10 obj_selected = 0; 11 while(0 == obj_selected) 12 { 13 obj_selected = get_rects(frame); 14 if (obj_selected == 0) 15 { 16 cout<<"You haven't selected any rectangles. "<<endl; 17 } 18 } 19 } 20 21 void MouseSelect::select_point(Mat &frame) 22 { 23 obj_selected = 0; 24 while(0 == obj_selected) 25 { 26 obj_selected = get_points(frame); 27 if (obj_selected == 0) 28 { 29 cout<<"You haven't selected any points."<<endl; 30 } 31 } 32 } 33 34 int MouseSelect::get_rects(Mat &frame) 35 { 36 char *win_name = "Init Frame"; 37 MouseSelectParams params; 38 params.win_name = win_name; 39 params.image = &frame; 40 params.n = 0; 41 params.mode = 2; 42 namedWindow(win_name,WINDOW_AUTOSIZE); 43 imshow(win_name,frame); 44 45 g_mousep = ¶ms; 46 setMouseCallback(win_name,on_mouse,0); 47 waitKey(0); 48 destroyWindow(win_name); 49 50 int x1,x2,y1,y2,w,h; 51 Rect rt; 52 Point pt1,pt2; 53 cout<<"Point Num: "<<params.pts.size()<<" Object Num: "<<params.n<<" Object Type: Rect."<<endl; 54 for(int i = 0;i < params.pts.size();i += 2) 55 { 56 pt1 = params.pts[i]; 57 pt2 = params.pts[i + 1]; 58 x1 = min(pt1.x,pt2.x); 59 x2 = max(pt1.x,pt2.x); 60 y1 = min(pt1.y,pt2.y); 61 y2 = max(pt1.y,pt2.y); 62 w = x2 - x1; 63 h = y2 - y1; 64 w = (w % 2)? w:w+1; 65 h = (h % 2)? h:h+1; 66 rt.x = x1; 67 rt.y = y1; 68 rt.width = w; 69 rt.height = h; 70 vRects.push_back(rt); 71 } 72 return params.n; 73 } 74 75 int MouseSelect::get_points(Mat &frame) 76 { 77 char *win_name = "Init Frame"; 78 MouseSelectParams params; 79 params.win_name = win_name; 80 params.image = &frame; 81 params.n = 0; 82 params.mode = 1; 83 84 namedWindow(win_name,1); 85 imshow(win_name,frame); 86 87 g_mousep = ¶ms; 88 setMouseCallback(win_name,on_mouse,0); 89 waitKey(0); 90 91 destroyWindow(win_name); 92 93 cout<<"Point Num: "<<params.pts.size()<<" Object Num: "<<params.n<<" Object Type: Point."<<endl; 94 for (int i = 0;i < params.n;i++) 95 { 96 vPoints.push_back(params.pts[i]); 97 } 98 return params.n; 99 } 100 101 void on_mouse(int event,int x,int y,int flags,void *param) 102 { 103 int n; 104 static bool pressed = false; 105 static Point cur_pt; 106 static Point prev_pt; 107 /* on left button press, remember first corner of rectangle around object */ 108 if (event == CV_EVENT_LBUTTONDOWN) 109 { 110 n = g_mousep->n; 111 if (n == MAX_OBJECTS) 112 { 113 return; 114 } 115 prev_pt.x = x; 116 prev_pt.y = y; 117 //cout<<prev_pt.x <<","<<prev_pt.y<<" "<<cur_pt.x<<","<<cur_pt.y<<endl; 118 pressed = true; 119 } 120 /* on left button up, finalise the rectangle and draw it in black */ 121 else if(event == CV_EVENT_LBUTTONUP && pressed) 122 { 123 /* 1 == mode for selecting points */ 124 if (1 == g_mousep->mode) 125 { 126 n = g_mousep->n; 127 if (n == MAX_OBJECTS) 128 { 129 return; 130 } 131 cur_pt.x = x; 132 cur_pt.y = y; 133 g_mousep->pts.push_back(cur_pt); 134 int radius = 3; 135 circle(*g_mousep->image,cur_pt,radius,CV_RGB(255,0,0)); 136 imshow(g_mousep->win_name,*g_mousep->image); 137 pressed = false; 138 g_mousep->n++; 139 } 140 /* 2 == mode for selecting rectangles */ 141 else if (2 == g_mousep->mode) 142 { 143 n = g_mousep->n; 144 if (n == MAX_OBJECTS) 145 { 146 return; 147 } 148 cur_pt.x = x; 149 cur_pt.y = y; 150 g_mousep->pts.push_back(prev_pt); 151 g_mousep->pts.push_back(cur_pt); 152 // cout<<prev_pt.x <<","<<prev_pt.y<<" "<<cur_pt.x<<","<<cur_pt.y<<endl; 153 rectangle(*g_mousep->image,prev_pt,cur_pt,CV_RGB(0,0,255)); 154 imshow(g_mousep->win_name,*g_mousep->image); 155 pressed = false; 156 g_mousep->n++; 157 } 158 else if (3 == g_mousep->mode) 159 { 160 n = g_mousep->n; 161 162 } 163 } 164 else if (event == CV_EVENT_MOUSEMOVE && pressed && CV_EVENT_FLAG_LBUTTON) 165 { 166 if (2 == g_mousep->mode) 167 { 168 n = g_mousep->n; 169 if (n == MAX_OBJECTS) 170 { 171 return; 172 } 173 cur_pt.x = x; 174 cur_pt.y = y; 175 Mat tmp = (*g_mousep->image).clone(); 176 //cout<<prev_pt.x <<","<<prev_pt.y<<" "<<cur_pt.x<<","<<cur_pt.y<<endl; 177 rectangle(tmp,prev_pt,cur_pt,CV_RGB(0,255,0)); 178 imshow(g_mousep->win_name,tmp); 179 } 180 } 181 }
代码分析:
on_mouse()回调函数,对应于一个显示窗口。
在回调函数处理鼠标事件,要理解一个鼠标点击过程,会产生多个事件。
例如:鼠标左击,对应着CV_EVENT_LBUTTONDOWN和CV_EVENT_LBUTTONUP。
在选择矩形框区域时,CV_EVENT_LBUTTONDOWN记录一个对角点;CV_EVENT_MOUSEMOVE记录选对矩形框的中间过程,并显示出来,便于用户调整选择的区域;CV_EVENT_LBUTTONUP记录另一个对角点,CV_EVENT_LBUTTONUP响应后,完成一个矩形框的选择。
在选择点时,只需记录CV_EVENT_LBUTTONUP事件响应的坐标。