Help us understand the problem. What is going on with this article?

OpenCV マウスイベント情報取得 setMouseCallback (クリックボタン判定、座標取得など)

More than 1 year has passed since last update.

環境

・Windows 7 Professional 64bit
・Visual Studio 2017 Version 15.7.0
・OpenCV 3.4.1

OpenCVの導入方法などは、下記を参照ください。
Visual Studio 2017 + OpenCV 3.4.1 + CUDA 10.0 + Intel TBB 2018U5 ビルド手順

マウスイベント

マウスイベントの情報を取得したりするには、setMouseCallback関数を
使用します。今回は、下記のマウスイベントを紹介します。

  • クリックボタン判定
    • 左ボタンクリック (EVENT_LBUTTONDOWN, EVENT_LBUTTONUP)
    • 中ボタンクリック (EVENT_MBUTTONDOWN, EVENT_MBUTTONUP)
    • 右ボタンクリック (EVENT_RBUTTONDOWN, EVENT_RBUTTONUP)
    • 左ボタンダブルクリック (EVENT_LBUTTONDBLCLK)
    • 中ボタンダブルクリック (EVENT_MBUTTONDBLCLK)
    • 右ボタンダブルクリック (EVENT_RBUTTONDBLCLK)
    • マウスホイール (EVENT_MOUSEWHEEL, getMouseWheelDelta)
  • 座標取得 (EVENT_MOUSEMOVE)
  • 応用 (矩形描画時の左上座標と右下座標の取得)

※実行結果をgifでも紹介していますが、ものによってはわかりづらいかもです。。。

クリックボタン判定

左ボタンクリック (EVENT_LBUTTONDOWN, EVENT_LBUTTONUP)

マウスの左ボタンクリック判定(ボタンを押したとき)には、EVENT_LBUTTONDOWNを使用します。

LeftMouseClick1.cpp
#include <opencv2\opencv.hpp>
#include <opencv_lib.hpp>

#include <iostream>
using namespace std;
using namespace cv;

void mouse_callback(int event, int x, int y, int flags, void *userdata)
{
    if (event == EVENT_LBUTTONDOWN) {
        cout << "Left mouse button is pressed." << endl;
    }
}

int main()
{
    Mat img = imread("./cpp_img.png");

    imshow("example", img);
    setMouseCallback("example", mouse_callback);
    waitKey();

    return 0;
}

LeftMouseClick1.gif

マウスの左ボタンクリック後判定(ボタンを離したとき)には、EVENT_LBUTTONUPを使用します。

LeftMouseClick2.cpp
#include <opencv2\opencv.hpp>
#include <opencv_lib.hpp>

#include <iostream>
using namespace std;
using namespace cv;

void mouse_callback(int event, int x, int y, int flags, void *userdata)
{
    if (event == EVENT_LBUTTONUP) {
        cout << "Left mouse button is released." << endl;
    }
}

int main()
{
    Mat img = imread("./cpp_img.png");

    imshow("example", img);
    setMouseCallback("example", mouse_callback);
    waitKey();

    return 0;
}

LeftMouseClick2.gif

マウスの左ボタンクリック状態判定(ボタンが押されている状態)には、EVENT_FLAG_LBUTTONを使用します。
EVENT_LBUTTONDOWNとの違いが見せにくいので、特に紹介はしません。。。
(使用したい場合は、event == EVENT_FLAG_LBUTTONに変更するだけです)

中ボタンクリック (EVENT_MBUTTONDOWN, EVENT_MBUTTONUP)

マウスの中ボタンクリック判定(ボタンを押したとき)には、EVENT_MBUTTONDOWNを使用します。

MiddleMouseClick1.cpp
#include <opencv2\opencv.hpp>
#include <opencv_lib.hpp>

#include <iostream>
using namespace std;
using namespace cv;

void mouse_callback(int event, int x, int y, int flags, void *userdata)
{
    if (event == EVENT_MBUTTONDOWN) {
        cout << "Middle mouse button is pressed." << endl;
    }
}

int main()
{
    Mat img = imread("./cpp_img.png");

    imshow("example", img);
    setMouseCallback("example", mouse_callback);
    waitKey();

    return 0;
}

MiddleMouseClick1.gif

マウスの中ボタンクリック後判定(ボタンを離したとき)には、EVENT_MBUTTONUPを使用します。

MiddleMouseClick2.cpp
#include <opencv2\opencv.hpp>
#include <opencv_lib.hpp>

#include <iostream>
using namespace std;
using namespace cv;

void mouse_callback(int event, int x, int y, int flags, void *userdata)
{
    if (event == EVENT_MBUTTONUP) {
        cout << "Middle mouse button is released." << endl;
    }
}

int main()
{
    Mat img = imread("./cpp_img.png");

    imshow("example", img);
    setMouseCallback("example", mouse_callback);
    waitKey();

    return 0;
}

MiddleMouseClick2.gif

マウスの中ボタンクリック状態判定(ボタンが押されている状態)には、EVENT_FLAG_MBUTTONを使用します。
左右ボタンと同様、違いがわかりにくいので特に紹介しません。。。

右ボタンクリック (EVENT_RBUTTONDOWN, EVENT_RBUTTONUP)

マウスの右ボタンクリック判定(ボタンを押したとき)には、EVENT_RBUTTONDOWNを使用します。

RightMouseClick1.cpp
#include <opencv2\opencv.hpp>
#include <opencv_lib.hpp>

#include <iostream>
using namespace std;
using namespace cv;

void mouse_callback(int event, int x, int y, int flags, void *userdata)
{
    if (event == EVENT_RBUTTONDOWN) {
        cout << "Right mouse button is pressed." << endl;
    }
}

int main()
{
    Mat img = imread("./cpp_img.png");

    imshow("example", img);
    setMouseCallback("example", mouse_callback);
    waitKey();

    return 0;
}

RightMouseClick1.gif

マウスの右ボタンクリック後判定(ボタンを離したとき)には、EVENT_RBUTTONUPを使用します。

RightMouseClick2.cpp
#include <opencv2\opencv.hpp>
#include <opencv_lib.hpp>

#include <iostream>
using namespace std;
using namespace cv;

void mouse_callback(int event, int x, int y, int flags, void *userdata)
{
    if (event == EVENT_RBUTTONUP) {
        cout << "Right mouse button is released." << endl;
    }
}

int main()
{
    Mat img = imread("./cpp_img.png");

    imshow("example", img);
    setMouseCallback("example", mouse_callback);
    waitKey();

    return 0;
}

RightMouseClick2.gif

マウスの右ボタンクリック状態判定(ボタンが押されている状態)には、EVENT_FLAG_RBUTTONを使用します。
左ボタンと同様、違いがわかりにくいので特に紹介しません。。。

左ボタンダブルクリック (EVENT_LBUTTONDBLCLK)

マウスの左ボタンダブルクリック判定(ボタンを2回押したとき)には、EVENT_LBUTTONDBLCLKを使用します。

以下のコードでは、わかりやすいように1回クリックしたときの判定EVENT_LBUTTONDOWNを入れています。実行結果からわかるように、2回目のクリックでダブルクリックの判定が出ていることがわかります。

LeftDoubleClick.cpp
#include <opencv2\opencv.hpp>
#include <opencv_lib.hpp>

#include <iostream>
using namespace std;
using namespace cv;

void mouse_callback(int event, int x, int y, int flags, void *userdata)
{
    if (event == EVENT_LBUTTONDOWN) {
        cout << "Left mouse button is pressed." << endl;
    }
    if (event == EVENT_LBUTTONDBLCLK) {
        cout << "Left mouse button is double clicked. " << endl;
    }
}

int main()
{
    Mat img = imread("./cpp_img.png");

    imshow("example", img);
    setMouseCallback("example", mouse_callback);
    waitKey();

    return 0;
}

LeftDoubleClick.gif

右ボタンダブルクリック (EVENT_RBUTTONDBLCLK)

マウスの右ボタンダブルクリック判定(ボタンを押したとき)には、EVENT_RBUTTONDBLCLKを使用します。

以下のコードでは、わかりやすいように1回クリックしたときの判定EVENT_RBUTTONDOWNを入れています。実行結果からわかるように、2回目のクリックでダブルクリックの判定が出ていることがわかります。

RightDoubleClick.cpp
#include <opencv2\opencv.hpp>
#include <opencv_lib.hpp>

#include <iostream>
using namespace std;
using namespace cv;

void mouse_callback(int event, int x, int y, int flags, void *userdata)
{
    if (event == EVENT_RBUTTONDOWN) {
        cout << "Right mouse button is pressed." << endl;
    }
    if (event == EVENT_RBUTTONDBLCLK) {
        cout << "Right mouse button is double clicked. " << endl;
    }
}

int main()
{
    Mat img = imread("./cpp_img.png");

    imshow("example", img);
    setMouseCallback("example", mouse_callback);
    waitKey();

    return 0;
}

RightDoubleClick.gif

マウスホイール (EVENT_MOUSEWHEEL, getMouseWheelDelta)

マウスのホイール判定には、EVENT_MOUSEWHEELを使用します。
ただし、このEVENT_MOUSEWHEELだけでは、ホイールを前にスクロールしたか後ろにスクロールしたかがわかりません。ここで、getMouseWheelDelta関数を使用して、スクロールの前後判定をします。具体的には、getMouseWheelDelta関数の引数にflagsを渡すことで、前にスクロールした場合は返り値が正に、後ろにスクロールした場合は返り値が負になります。ただし、公式ドキュメントのNoteには、Windowsのみのサポートになるようです。
※一応、ここによるとLinuxでも返り値は違うみたいですが、正負の判定はできるみたいです。私は試していません。

MouseWheel.cpp
#include <opencv2\opencv.hpp>
#include <opencv_lib.hpp>

#include <iostream>
using namespace std;
using namespace cv;

void mouse_callback(int event, int x, int y, int flags, void *userdata)
{
    if (event == EVENT_MOUSEWHEEL) {
        if (getMouseWheelDelta(flags)>0) {
            cout << "Mouse wheel is forward." << endl;
        }
        if (getMouseWheelDelta(flags)<0) {
            cout << "Mouse wheel is backward." << endl;
        }
    }
}

int main()
{
    Mat img = imread("./cpp_img.png");

    imshow("example", img);
    setMouseCallback("example", mouse_callback);
    waitKey();

    return 0;
}

MouseWheel.gif

前後のスクロールだけでなく、左右のスクロール判定をするEVENT_MOUSEHWHEELもありますが、試せる環境がないので試していないです。。。
※一応、ホイールを左右に動かしてみて試しましたが、それではダメっぽいです。なのでクリックではなく、スクロールじゃないとダメみたいです。

座標取得 (EVENT_MOUSEMOVE)

マウス移動時の座標取得には、EVENT_MOUSEMOVEを使用します。
ほぼ、下記のサイトと変わりませんが結果とともに載せておきます。。。
Can I get the mouse position in OpenCV without a mouse event?

GetMousePos.cpp
#include <opencv2\opencv.hpp>
#include <opencv_lib.hpp>

#include <iostream>
using namespace std;
using namespace cv;

void mouse_callback(int event, int x, int y, int flags, void *userdata)
{
    if (event == EVENT_MOUSEMOVE) {
        cout << "(" << x << ", " << y << ")" << endl;
    }
}

int main()
{
    Mat img = imread("./cpp_img.png");

    imshow("example", img);
    setMouseCallback("example", mouse_callback);
    waitKey();

    return 0;
}

GetMousePos.gif

応用 (矩形描画時の左上座標と右下座標の取得)

EVENT_LBUTTONDOWN, EVENT_LBUTTONUP, EVENT_MOUSEMOVEの3つを使用して矩形描画時の左上座標と右下座標の取得をしています。
※ウィンドウ外のマイナス値などのエラー処理は特にしていません。

DrawRectangle.cpp
#include <opencv2\opencv.hpp>
#include <opencv_lib.hpp>

#include <iostream>
using namespace std;
using namespace cv;

Rect2i rectangle_value;


void mouse_callback(int event, int x, int y, int flags, void *userdata)
{
    bool *isClick = static_cast<bool *>(userdata);
    if (event == EVENT_LBUTTONDOWN) {
        *isClick = true;
        cout << "Draw rectangle\n"
            << " start position (x, y) : " << x << ", " << y << endl;
        rectangle_value = Rect2i(x, y, 0, 0);
    }
    if (event == EVENT_LBUTTONUP) {
        *isClick = false;
        cout << " end   position (x, y) : " << x << ", " << y << endl;
        rectangle_value.width = x - rectangle_value.x;
        rectangle_value.height = y - rectangle_value.y;
    }
    if (event == EVENT_MOUSEMOVE) {
        if (*isClick) {
            rectangle_value.width = x - rectangle_value.x;
            rectangle_value.height = y - rectangle_value.y;
        }
    }
}

int main()
{
    Mat img = imread("./cpp_img.png");
    Mat draw_img = img.clone();
    string window_name = "example";
    bool isClick = false;
    int key;

    imshow(window_name, img);
    setMouseCallback(window_name, mouse_callback, &isClick);
    for (;;) {
        key = 0;

        // 左ボタンが押されたら描画開始
        if (isClick == true) {
            rectangle(draw_img, rectangle_value, Scalar(255, 0, 0), 3, CV_AA);
        }

        imshow(window_name, draw_img);
        draw_img = img.clone();

        // qキーが押されたら終了
        key = waitKey(1);
        if (key == 'q')
            break;
    }

    return 0;
}

DrawRectangle.gif

ちなみに矩形での切り抜き関数SelectROIなどは、このマウスイベントがうまく使われているので、参考になります。

参考サイト

[1]:Can I get the mouse position in OpenCV without a mouse event?
[2]:OpenCV: High-level GUI
[3]:opencv/roiSelector.cpp at master · opencv/opencv
[4]:ハードウェアとか研究所: OpenCV(3.x系)でマウスホイールを使う(2.4.x系は不可)

East_san
OpenCV関係の記事が、多くなるかもしれません。 学生時代は、コンピュータビジョンの研究をしていました。 使用言語:C/C++, Python 過去:MATLAB、Processing, R
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away