Opencv — 17鼠标操作与响应

    函数介绍:
    1、绑定回调事件函数:

    void setMouseCallback(const String& winname, MouseCallback onMouse, void* userdata = 0);
    功能:为指定的窗口设置鼠标处理程序
    参数:
    winname – Window name (窗体的名称)
    onMouse  - 鼠标事件的回调函数。
    param 传递给回调函数的可选参数。
   
    2、鼠标事件的回调函数:MouseCallback

    typedef void (*MouseCallback)(int event, int x, int y, int flags, void* userdata);
    
    event cv::MouseEventTypes 的常量之一。
    x     鼠标事件的x坐标。
    y     鼠标事件的y坐标。
    flags cv::MouseEventFlags 的常量之一。
    userdata 可选参数。

    cv::MouseEventFlags都有这些:
    EVENT_FLAG_ALTKEY = 32		摁住Alt
    EVENT_FLAG_CTRLKEY = 8		摁住Ctrl

    EVENT_FLAG_LBUTTON = 1		摁住左键
    EVENT_FLAG_MBUTTON = 4		摁住中键
    EVENT_FLAG_RBUTTON = 2		摁住右键
    EVENT_FLAG_SHIFTKEY = 16	摁住Shift

    EVENT_LBUTTONDBLCLK = 7		左键双击
    EVENT_LBUTTONDOWN = 1		左键击下
    EVENT_LBUTTONUP = 4			左键弹起
    EVENT_MBUTTONDBLCLK = 9		中键双击
    EVENT_MBUTTONDOWN = 3		中键击下
    EVENT_MBUTTONUP = 6			中键弹起
    EVENT_MOUSEHWHEEL = 11		滚动条向左,flags>0。向右,flags<0
    EVENT_MOUSEMOVE = 0			鼠标移动
    EVENT_MOUSEWHEEL = 10		滚动条向上,flags>0。向下,flags<0
    EVENT_RBUTTONDBLCLK = 8		中键双击
    EVENT_RBUTTONDOWN = 2		中键击下
    EVENT_RBUTTONUP = 5			中键弹起

17_opencv_mat.h 代码如下:

#pragma once

#ifndef _17_OPENCV_MAT_H
#define _17_OPENCV_MAT_H

#include <opencv2/opencv.hpp>

using namespace cv;

class QuickDemo {
public:
	void mouse_drawing_demo(Mat& image);
};

#endif

17_opencv_mat.cpp 代码如下:

#include "17_opencv_mat.h"

//借助全局变量来记录状态
Point sp(-1,-1);  //开始位置
Point ep(-1,-1);  //结束位置
static void ondraw(int event, int x, int y, int flags, void* userdata) 
{
    Mat image = *((Mat*)userdata);
    //如果鼠标左键按下
    if (event == EVENT_LBUTTONDOWN)
    {
        sp.x = x;
        sp.y = y;
        std::cout <<"start point: "<< sp << std::endl;
    }
    //如果鼠标左键弹起
    else if (event == EVENT_LBUTTONUP)
    {
        ep.x = x;
        ep.y = y;
        std::cout << "end point: " << ep << std::endl;
        int dx = ep.x - sp.x;
        int dy = ep.y - sp.y;
        if (dx > 0 && dy > 0)
        {
            Rect box(sp.x, sp.y, dx, dy);
            rectangle(image, box, Scalar(0, 0, 255), 2, LINE_8, 0);
            imshow("鼠标绘制", image);
        }
    }
}

void QuickDemo::mouse_drawing_demo(Mat &image)
{
    namedWindow("鼠标绘制", WINDOW_AUTOSIZE);

    setMouseCallback("鼠标绘制", ondraw,(void*)(&image));

    imshow("鼠标绘制",image);
}

test.cpp 代码如下:

#include <iostream>
#include "17_opencv_mat.h"

using namespace std;

int main(int argc,char** argv)
{
    //使用Mat(matrix -- 矩阵)这种类型来读取通过指定路径的图像信息并存放到变量picture中。
    //图像从本质上来说都是二维的数组(矩阵)
    Mat picture = imread("鱼头人.jpeg", IMREAD_COLOR);

    if (picture.empty())
    {
        cout << "could not Load image." << endl;
        system("pause");
        return -1;
    }
    
    // namedWindow("输入窗口",WINDOW_FREERATIO);
   
    imshow("输入窗口", picture);

    QuickDemo qd;
    qd.mouse_drawing_demo(picture);

    waitKey(0);
    destroyAllWindows();

    system("pause");
    return 0;
}

运行结果如下:
在这里插入图片描述
与此同时,
在这里插入图片描述
但是,这样的绘制有一个弊端就是无法看到绘制的过程。

我们可以这样改进:
17_opencv_mat.cpp 代码如下:

#include "17_opencv_mat.h"

//借助全局变量来记录状态
Point sp(-1,-1);  //开始位置
Point mp(-1, -1); //移动位置
static void ondraw(int event, int x, int y, int flags, void* userdata) 
{
    Mat image = *((Mat*)userdata);
    //如果鼠标左键按下
    if (event == EVENT_LBUTTONDOWN)
    {
        sp.x = x;
        sp.y = y;
        std::cout <<"start point: "<< sp << std::endl;
    }
    //如果鼠标左键弹起
    else if (event == EVENT_LBUTTONUP)
    {
        //为下一次绘制做好准备
        sp.x = -1;
        sp.y = -1;
    }
    else if (event == EVENT_MOUSEMOVE)
    {
        if (sp.x > 0 && sp.y > 0)
        {
            mp.x = x;
            mp.y = y;
            std::cout << "moving point: " << mp << std::endl;
            int dx = mp.x - sp.x;
            int dy = mp.y - sp.y;
            if (dx > 0 && dy > 0)
            {
                Rect box(sp.x, sp.y, dx, dy);
                rectangle(image, box, Scalar(0, 0, 255), 2, LINE_8, 0);
                imshow("鼠标绘制", image);
            }
        }
    }
}

void QuickDemo::mouse_drawing_demo(Mat &image)
{
    namedWindow("鼠标绘制", WINDOW_AUTOSIZE);

    setMouseCallback("鼠标绘制", ondraw,(void*)(&image));

    imshow("鼠标绘制",image);
}

改进后的效果如下:
在这里插入图片描述
这是因为它把我移动的过程中的点都进行绘制了。

那怎么解决呢?

#include "17_opencv_mat.h"

//借助全局变量来记录状态
Point sp(-1,-1);  //开始位置
Point mp(-1, -1); //移动位置
Point ep(-1,-1);  //结束位置

Mat temp;
static void ondraw(int event, int x, int y, int flags, void* userdata) 
{
    Mat image = *((Mat*)userdata);
    //如果鼠标左键按下
    if (event == EVENT_LBUTTONDOWN)
    {
        sp.x = x;
        sp.y = y;
        std::cout <<"start point: "<< sp << std::endl;
    }
    //如果鼠标左键弹起
    else if (event == EVENT_LBUTTONUP)
    {
        ep.x = x;
        ep.y = y;
        std::cout << "end point: " << ep << std::endl;
        //为下一次绘制做好准备
        sp.x = -1;
        sp.y = -1;
    }
     //如果鼠标左键被按下后移动
    else if (event == EVENT_MOUSEMOVE)
    {
        if (sp.x > 0 && sp.y > 0)
        {
            mp.x = x;
            mp.y = y;
            std::cout << "moving point: " << mp << std::endl;
            int dx = mp.x - sp.x;
            int dy = mp.y - sp.y;
            if (dx > 0 && dy > 0)
            {
                Rect box(sp.x, sp.y, dx, dy);
                //每次绘制之前就将上一次在原图上绘制了矩形的那个图給覆盖成原图,
                //从而只保留最后一次绘制。
                temp.copyTo(image);
                rectangle(image, box, Scalar(0, 0, 255), 2, LINE_8, 0);
                imshow("鼠标绘制", image);
            }
        }
    }
}

void QuickDemo::mouse_drawing_demo(Mat &image)
{
    namedWindow("鼠标绘制", WINDOW_AUTOSIZE);

    setMouseCallback("鼠标绘制", ondraw,(void*)(&image));

    imshow("鼠标绘制",image);

    temp = image.clone();
}

最终运行结果如下:

在这里插入图片描述
现在,我们获得了矩形的起始坐标和结束坐标,我们就可以将矩形部分(ROI区域)的图像给提取出来,应该怎么做呢?

...
temp.copyTo(image); 

imshow("ROI区域", image(box));//只需要添加这句代码

rectangle(image, box, Scalar(0, 0, 255), 2, LINE_8, 0);
...

运行代码如下:
在这里插入图片描述
需要注意的是,最后鼠标的结束坐标不能在窗口外,否则会报错。

因此需要进行改进,改进如下:

#include "17_opencv_mat.h"

//借助全局变量来记录状态
Point sp(-1,-1);  //开始位置
Point mp(-1, -1); //移动位置
Point ep(-1,-1);  //结束位置

Mat temp;
static void ondraw(int event, int x, int y, int flags, void* userdata) 
{
    Mat image = *((Mat*)userdata);

    int w = image.cols; // 列
    int h = image.rows; // 行

    //如果鼠标左键按下
    if (event == EVENT_LBUTTONDOWN)
    {
        sp.x = x;
        sp.y = y;
        std::cout <<"start point: "<< sp << std::endl;
    }
    //如果鼠标左键弹起
    else if (event == EVENT_LBUTTONUP)
    {
        ep.x = x;
        ep.y = y;

        if (ep.x > w || ep.y > h)
        {
            std::cout<< "End Beyond the border" << std::endl;

            //为下一次绘制做好准备
            sp.x = -1;
            sp.y = -1;
        }
        else
        { 
            std::cout << "end point: " << ep << std::endl;
            //为下一次绘制做好准备
            sp.x = -1;
            sp.y = -1;
        }
    }
    else if (event == EVENT_MOUSEMOVE)
    {
        if (sp.x > 0 && sp.y > 0)
        {
            mp.x = x;
            mp.y = y;
            if (mp.x > w || mp.y > h)
            {
                std::cout << "Move Beyond the border" << std::endl;

                int dx = w - sp.x;
                int dy = h - sp.y;
                if (dx > 0 && dy > 0)
                {
                    Rect box(sp.x, sp.y, dx, dy);
                    //每次绘制之前就将上一次在原图上绘制了矩形的那个图給覆盖成原图,
                    //从而只保留最后一次绘制。
                    temp.copyTo(image);
                    imshow("ROI区域", image(box));
                    rectangle(image, box, Scalar(0, 0, 255), 2, LINE_8, 0);
                    imshow("鼠标绘制", image);
                }
            }
            else
            { 
                int dx = mp.x - sp.x;
                int dy = mp.y - sp.y;
                if (dx > 0 && dy > 0)
                {
                    Rect box(sp.x, sp.y, dx, dy);
                    //每次绘制之前就将上一次在原图上绘制了矩形的那个图給覆盖成原图,
                    //从而只保留最后一次绘制。
                    temp.copyTo(image);
                    imshow("ROI区域", image(box));
                    rectangle(image, box, Scalar(0, 0, 255), 2, LINE_8, 0);
                    imshow("鼠标绘制", image);
                }
            }
        }
    }
}

void QuickDemo::mouse_drawing_demo(Mat &image)
{
    namedWindow("鼠标绘制", WINDOW_AUTOSIZE);

    setMouseCallback("鼠标绘制", ondraw,(void*)(&image));

    imshow("鼠标绘制",image);

    temp = image.clone();
}

效果如下:
在这里插入图片描述

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>