-->

2017年2月18日土曜日

OpenCV(3.x系)でマウスホイールを使う(2.4.x系は不可)

いつの間にかOpenCVでマウスホイールが使えるようになっていました。
どうやらOpenCV3.xから追加されたみたいです。
ウィンドウ上でのマウスホイールの変化がイベントとして検出できます。
※コールバック関数内で"EVENT_MOUSEWHEEL"で引っ掛ける
ホイールがどの方向に回ったのかはgetMouseWheelDelta関数の返り値で知ることができます。
まず、その値が正の値か負の値かで回転した方向がわかります。
例えば、Windows7で値を観測した場合、以下のような値が得られます。
 ホイールを奥に回す → 120(正の値)
 ホイールを手前に回す → -120(負の値)
回転量は120を単位(1ノッチ)とした値で取得できます。
マウスの設定にもよるとは思いますが、通常は"120"もしくは"-120"の値しか見えないのですが、
勢いよくホイールを回すとたまに"240"とか"-240"という値を見ることができます。

この関数はWindowsだけしかサポートしてないって書いてあったのですが、同様のことをLinux(ラズパイ3)で実行すると以下のようになります。

 ホイールを奥に回す → -1(負の値)
 ホイールを手前に回す → 1(正の値)

値の符号は反転していますが、使えないことはなさそうです。
その他、Linuxでは"EVENT_MOUSEWHEEL"イベント発生時のマウスポインタ座標がうまく取得できませんでした。
※コールバック関数内では常時(x, y) -> (-3, -3)が取得される
※Windows7では、イベント発生時のマウスポインタの位置が正常にx&yに格納された
やはり完全にサポートしているわけではなさそうです。

ホイール動作が使えるようになると、表示している画像の拡大や縮小処理を直感的に行えるので便利です。

以下はラズベリーパイ3で使用しているコードで、ホイールイベントの使用サンプルコードです。
前述の通り、ラズベリーパイ3ではx&y座標がうまく取得できないので、"CV_EVENT_MOUSEMOVE"イベント時のx&y座標を保持しておくことで代替しています。
使用しているOpenCVのバージョンは"3.2"です。

#include <stdio.h>

// for OpenCV
#include "opencv2/opencv.hpp"

// マウスポインタの座標保持
cv::Point MousePointer(0,0);

void mouse_callback(int event, int x, int y, int flags, void* data);

int main(int argc, char *argv[]) {

    // 画像ロード
    cv::Mat image = cv::imread("test.png");
    if(image.empty()) {
        printf("[error] : can't load image...\n");
        return 0;
    }

    // ウィンドウの作成
    char window_name[256] = "test";
    cv::namedWindow(window_name, CV_WINDOW_NORMAL);
    cv::moveWindow(window_name, 0, 0);
    cv::resizeWindow(window_name, 640, 480);

    // マウスコールバック関数設定
    cv::setMouseCallback(window_name, mouse_callback, (void *)NULL);

    // 画像表示
    cv::imshow(window_name, image);

    // キー入力待ち
    int key = cv::waitKey(0);

    cv::destroyWindow(window_name);

    return 0;

}

// マウスコールバック関数
void mouse_callback(int event, int x, int y, int flags, void* data) {
    switch (event){
        case cv::EVENT_MOUSEMOVE:
            // アドレス確保
            MousePointer = cv::Point(x,y);
            break;
        case cv::EVENT_MOUSEWHEEL:
            // getMouseWheelDelta(flags)の返り値
            // 正か負の値が得られる
            // どちらがどの回転方向に対応するかは環境依存っぽい
            printf("[debug] : mouse wheel. x->%d, y->%d, getMouseWheelDelta -> %d\n", MousePointer.x, MousePointer.y, cv::getMouseWheelDelta(flags));
            break;
    }
}