Opencv实现鼠标事件与窗口互动功能过程
明月醉窗台 人气:01.opencv API及参数介绍
cv::setMouseCallback(const string& windowname, MouseCallback onMouse, void* userdata=0) { .... } // windowname: 要操作的窗口名称 // onMouse: 鼠标事件函数,鼠标事件发生以后,要执行的回调函数。函数原型是 // void onMouse(int event, int x, int y, int flags, void * para) // userdata: 回调函数的参数
鼠标回调函数介绍:
void onMouse(int event, int x, int y, int flags, void *para) // int event: 鼠标事件,见后续说明 // x, y 是鼠标在图像坐标系中的坐标 // flags : // para: 是用户传递到回调函数中的参数
截取感兴趣区域ROI:
cv::selectROIs("MultiTracker", frame, bboxes_, showCrosshair, fromCenter); // “MultiTracker” : windowsName // frame: 当前画面;格式为cv::Mat // bboxes: 要存储的框框格式为:std::vector<cv::Rect> // showCrosshair:默认为True // fromCenter:从中心点还是从对角点,默认为false,为true时,选框从中心点开始
涉及的相关参数:
// EVENT的参数定义 enum { CV_EVENT_MOUSEMOVE =0,//滑动 CV_EVENT_LBUTTONDOWN =1,//左键点击 CV_EVENT_RBUTTONDOWN =2,//右键点击 CV_EVENT_MBUTTONDOWN =3,//中键点击 CV_EVENT_LBUTTONUP =4,//左键放开 CV_EVENT_RBUTTONUP =5,//右键放开 CV_EVENT_MBUTTONUP =6,//中键放开 CV_EVENT_LBUTTONDBLCLK =7,//左键双击 CV_EVENT_RBUTTONDBLCLK =8,//右键双击 CV_EVENT_MBUTTONDBLCLK =9//中键双击 };
enum { CV_EVENT_FLAG_LBUTTON =1,//左键拖拽 CV_EVENT_FLAG_RBUTTON =2,//右键拖拽 CV_EVENT_FLAG_MBUTTON =4,//中键拖拽 CV_EVENT_FLAG_CTRLKEY =8,//按CTRL不放 CV_EVENT_FLAG_SHIFTKEY =16,//按SHIFT不放 CV_EVENT_FLAG_ALTKEY =32//按ALT不放 };
或者python-opencv中的参数也相同:
# mouse callback function def mouse_event(event, x, y, flags, param): ''' :param event: 点击事件标识 :param x: 坐标x :param y: 坐标y :param flags:按键事件标识 :param param: 携带参数 :return: ''' if flags == cv2.EVENT_FLAG_ALTKEY: print('摁住Alt') if flags == cv2.EVENT_FLAG_CTRLKEY: print('摁住Ctrl') if flags == cv2.EVENT_FLAG_SHIFTKEY: print('摁住Shift') if flags == cv2.EVENT_FLAG_LBUTTON: print('摁住左键') if flags == cv2.EVENT_FLAG_MBUTTON: print('摁住中键') if flags == cv2.EVENT_FLAG_RBUTTON: print('摁住右键') if event == cv2.EVENT_LBUTTONDBLCLK: print('左键双击') if event == cv2.EVENT_MBUTTONDBLCLK: print('中键双击') if event == cv2.EVENT_RBUTTONDBLCLK: print('右键双击') if event == cv2.EVENT_LBUTTONDOWN: print('左键击下') if event == cv2.EVENT_LBUTTONUP: print('左键弹起') if event == cv2.EVENT_MBUTTONDOWN: print('中键击下') if event == cv2.EVENT_MBUTTONUP: print('中键弹起') if event == cv2.EVENT_RBUTTONDOWN: print('右键击下') if event == cv2.EVENT_RBUTTONUP: print('右键弹起') if event == cv2.EVENT_MOUSEWHEEL: if flags > 0: print('向前滚动') else: print('向后滚动') if event == cv2.EVENT_MOUSEHWHEEL: if flags > 0: print('向左滚动') # 按住Alt else: print('向右滚动') if __name__ == '__main__': img = np.mat(np.zeros((300, 300, 3), np.uint8)) # 构造一个像素值全为1的图形 cv2.namedWindow('mouse') #命名窗口为mouse cv2.setMouseCallback('mouse', mouse_event) # 窗口与回调函数绑定 while True: cv2.imshow('mouse', img) if cv2.waitKey(1) & 0xFF == ord('q'): # 摁下q退出 break cv2.destroyAllWindows() #销毁所有窗口
2.一个可以绘制矩形和控制缩放的demo
#include <opencv2/opencv.hpp> using namespace std; using namespace cv; #define WINNAME "画板" struct MouseParam { Mat img; //用于画好一个后显示 Mat imgZoomBackup; //用于zoom的还原备份 Mat imgTmp; //用于实时显示 Mat imgBackup; //清空,显示最初的图 Point pt1; Point pt2; bool mouseLflag; float scale; }; //绘制矩形 void draw_rectangle(Mat& img, const Point& pt1, const Point& pt2) { rectangle(img, pt1, pt2, Scalar(0, 255, 0), 1, 0, 0); } //绘制十字线:即过一点的水平、竖直线 void draw_crossline(Mat& img, const Point& pt) { int width = img.cols; int height = img.rows; cv::Point ptv1; cv::Point ptv2; cv::Point pth1; cv::Point pth2; ptv1 = cv::Point(pt.x, 0); ptv2 = cv::Point(pt.x, height); pth1 = cv::Point(0, pt.y); pth2 = cv::Point(width, pt.y); cv::line(img, ptv1, ptv2, Scalar(255, 255, 0), 1); cv::line(img, pth1, pth2, Scalar(255, 255, 0), 1); } /* * func:实现图像绕某一点进行缩放功能 */ void zoom(Mat& img, const Mat& srcimg, const Point& pt, const float scale) { int x1, y1, x2, y2; int width, height; width = (int)(srcimg.cols * scale / 2.0); height = (int)(srcimg.rows * scale / 2.0); x1 = max(pt.x - width, 0); y1 = max(pt.y - height, 0); x2 = min(pt.x + width, srcimg.cols); y2 = min(pt.y + height, srcimg.rows); Rect zoomRect(Point(x1, y1), Point(x2, y2)); img = srcimg(zoomRect).clone(); } void on_mouse(int event, int x, int y, int flags, void* param) { MouseParam* par = (MouseParam*)param; Point pt(x, y); double value; float step = 0.05; if (event == EVENT_RBUTTONDOWN) //按下右键,重画 { par->img = par->imgBackup.clone(); } else if (event == EVENT_LBUTTONDOWN) //按下左键 { par->pt1 = pt; par->pt2 = pt; par->mouseLflag = true; } else if (event == EVENT_MOUSEMOVE && flags == EVENT_FLAG_LBUTTON) //按下左键并移动 { par->pt2 = pt; } else if (event == EVENT_LBUTTONUP)//左键放开进行绘制矩形框 { par->pt2 = pt; draw_rectangle(par->img, par->pt1, par->pt2); //par->imgZoomBackup = par->img.clone(); par->mouseLflag = false; } else if (event == EVENT_MOUSEMOVE) //鼠标移动将显示十字线 { par->pt1 = pt; } else if (event == EVENT_MOUSEWHEEL) //鼠标滚动 { value = getMouseWheelDelta(flags); if (value > 0) //滚轮向前滚动,设为缩小 par->scale += step; else if (value < 0) //鼠标向后滚动,设为放大 par->scale -= step; par->scale = max((float)0.3, par->scale); par->scale = min((float)1.0, par->scale); zoom(par->img, par->imgZoomBackup, par->pt1, par->scale); } } int main() { //Mat img(512, 512, CV_8UC3, Scalar::all(255)); Mat img = imread("./imgs/1/L.png"); MouseParam mouseParam; mouseParam.img = img.clone(); mouseParam.imgBackup = img.clone(); mouseParam.imgZoomBackup = img.clone(); mouseParam.mouseLflag = false; float step = 0.05; mouseParam.scale = 1.0; namedWindow(WINNAME, 0); setMouseCallback(WINNAME, on_mouse, &mouseParam); int key; while (1) { mouseParam.imgTmp = mouseParam.img.clone(); draw_crossline(mouseParam.imgTmp, mouseParam.pt1); if (mouseParam.mouseLflag == true) draw_rectangle(mouseParam.imgTmp, mouseParam.pt1, mouseParam.pt2); imshow(WINNAME, mouseParam.imgTmp); key = waitKey(40); if (key == 27) { break; } else if (key == toascii('q')) { mouseParam.scale -= step; zoom(mouseParam.img, mouseParam.imgZoomBackup, mouseParam.pt1, mouseParam.scale); } else if (key == toascii('e')) { mouseParam.scale += step; zoom(mouseParam.img, mouseParam.imgZoomBackup, mouseParam.pt1, mouseParam.scale); } } return 0; }
3.图象中画点
- 对于拼接后的图像显示窗口,依次绘制左右图中的对应点程序
- 右键删除尚未调通
std::vector<cv::Point2f>pointsL; //依次存放左右图中的对应点 std::vector<cv::Point2f>pointsR; cv::Mat mask; cv::Point2f p_; //鼠标回调函数 void on_mouse(int event, int x, int y, int flags, void* param) { float cols = mask.cols/2; int rad = 6; if (event == cv::EVENT_LBUTTONUP || !(flags & cv::EVENT_FLAG_LBUTTON)) cv::Point2f pt = cv::Point2f(-10, -10); else if (event == cv::EVENT_LBUTTONDOWN) { cv::Point2f pt(x, y); if (pt.x>=0&&pt.x <= cols) { pointsL.push_back(pt); cv::circle(mask, pt, rad, cv::Scalar(0, 255, 0), 2); } else { pointsR.push_back(pt); cv::circle(mask, pt, rad, cv::Scalar(0, 0, 255), 2); } //cv::circle(mask, pt, 2, cv::Scalar(0, 255, 0), 2); cv::imshow("image", mask); } else if (event == cv::EVENT_RBUTTONDOWN) //按下右键,重画 { cv::Point2f pt(x, y); if (pointsL.size()>0) { pointsL.pop_back(); } else if (pointsR.size() > 0) { pointsR.pop_back(); } } std::cout << "pointsL" << std::endl; for (int i = 0; i < pointsL.size(); ++i) { std::cout << pointsL[i] << std::endl; } std::cout << "pointsR" << std::endl; for (int i = 0; i < pointsR.size(); ++i) { std::cout << pointsR[i] << std::endl; } }
4.opencv例程分水岭分割中的界面应用
鼠标时间包含绘制直线的交互操作
static void help(char** argv) { cout << "\nThis program demonstrates the famous watershed segmentation algorithm in OpenCV: watershed()\n" "Usage:\n" << argv[0] << " [image_name -- default is fruits.jpg]\n" << endl; cout << "Hot keys: \n" "\tESC - quit the program\n" "\tr - restore the original image\n" "\tw or SPACE - run watershed segmentation algorithm\n" "\t\t(before running it, *roughly* mark the areas to segment on the image)\n" "\t (before that, roughly outline several markers on the image)\n"; } Mat markerMask, img; Point prevPt(-1, -1); static void onMouse(int event, int x, int y, int flags, void*) { if (x < 0 || x >= img.cols || y < 0 || y >= img.rows) return; if (event == EVENT_LBUTTONUP || !(flags & EVENT_FLAG_LBUTTON)) prevPt = Point(-1, -1); else if (event == EVENT_LBUTTONDOWN) prevPt = Point(x, y); else if (event == EVENT_MOUSEMOVE && (flags & EVENT_FLAG_LBUTTON)) { Point pt(x, y); if (prevPt.x < 0) prevPt = pt; line(markerMask, prevPt, pt, Scalar(12,234,12), 40, 8, 0); line(img, prevPt, pt, Scalar(12,234,12), 40, 8, 0); prevPt = pt; imshow("image", img); } } int main(int argc, char** argv) { cv::CommandLineParser parser(argc, argv, "{help h | | }{ @input | fruits.jpg | }"); if (parser.has("help")) { help(argv); return 0; } //string filename = samples::findFile(parser.get<string>("@input")); Mat img0 = imread("..."), imgGray; if (img0.empty()) { cout << "Couldn't open image "; help(argv); return 0; } help(argv); //img0 = img0(Rect(1000, 1000, 800, 800)); namedWindow("image", WINDOW_NORMAL); img0.copyTo(img); cvtColor(img, markerMask, COLOR_BGR2GRAY); cvtColor(markerMask, imgGray, COLOR_GRAY2BGR); markerMask = Scalar::all(0); imshow("image", img); setMouseCallback("image", onMouse, 0); for (;;) { char c = (char)waitKey(0); if (c == 27) break; if (c == 'r') { markerMask = Scalar::all(0); img0.copyTo(img); namedWindow("image", WINDOW_NORMAL); imshow("image", img); } if (c == 'w' || c == ' ') { //对绘制完成的图像进行下一步操作 //须在键入相应按键后执行 } } return 0; }
加载全部内容