OpenCV 마우스 이벤트 처리하기

프로그래밍/opencv 2013.02.14 11:11

OpenCV에서 마우스를 사용하는 방법입니다. (mouse event handling)

1. 예제 코드

2. 예제 코드 다운로드

3. 실행 예




1. 예제 코드


아래 예는 opencv에서 마우스를 처리하기 위해 필요한 모든 정보를 포함하고 있습니다.


///////////////////////////////////////////////////////////////////////

// OpenCV Mouse event handling example.

// Written by darkpgmr (http://darkpgmr.tistory.com), 2013


#include "stdafx.h"

#include "windows.h"

#include <iostream>

#include "use_opencv.h"


using namespace std;

using namespace cv;


class SampleClass

{

public:

        void HandleEvent(int evt, int x, int y, int flags);

};


void SampleClass::HandleEvent(int evt, int x, int y, int flags)

{

        if( evt == CV_EVENT_LBUTTONDOWN )

        {

                cout << "CV_EVENT_LBUTTONDOWN: ";

                cout << "x=" << x << ", y=" << y << endl;

        }

        else if( evt == CV_EVENT_LBUTTONUP )

        {

                cout << "CV_EVENT_LBUTTONUP: ";

                cout << "x=" << x << ", y=" << y << endl;

        }

        else if( evt == CV_EVENT_MOUSEMOVE )

        {

//                cout << "CV_EVENT_MOUSEMOVE: ";

//                cout << "x=" << x << ", y=" << y << endl;

        }

        else if( evt == CV_EVENT_RBUTTONDOWN )

        {

                cout << "CV_EVENT_RBUTTONDOWN: ";

                cout << "x=" << x << ", y=" << y << endl;

        }

        else if( evt == CV_EVENT_RBUTTONUP )

        {

                cout << "CV_EVENT_RBUTTONUP: ";

                cout << "x=" << x << ", y=" << y << endl;

        }

        else if( evt == CV_EVENT_MBUTTONDOWN )

        {

                cout << "CV_EVENT_MBUTTONDOWN: ";

                cout << "x=" << x << ", y=" << y << endl;

        }

        else if( evt == CV_EVENT_MBUTTONUP )

        {

                cout << "CV_EVENT_MBUTTONUP: ";

                cout << "x=" << x << ", y=" << y << endl;

        }

        else if( evt == CV_EVENT_LBUTTONDBLCLK )

        {

                cout << "CV_EVENT_LBUTTONDBLCLK: ";

                cout << "x=" << x << ", y=" << y << endl;

        }

        else if( evt == CV_EVENT_RBUTTONDBLCLK )

        {

                cout << "CV_EVENT_RBUTTONDBLCLK: ";

                cout << "x=" << x << ", y=" << y << endl;

        }


        if(flags&CV_EVENT_FLAG_LBUTTON)

        {

                cout << "\tCV_EVENT_FLAG_LBUTTON" << endl;

        }

        if(flags&CV_EVENT_FLAG_RBUTTON )

        {

                cout << "\tCV_EVENT_FLAG_RBUTTON" << endl;

        }

        if(flags&CV_EVENT_FLAG_MBUTTON )

        {

                cout << "\tCV_EVENT_FLAG_MBUTTON" << endl;

        }

        if(flags&CV_EVENT_FLAG_CTRLKEY )

        {

                cout << "\tCV_EVENT_FLAG_CTRLKEY" << endl;

        }

        if(flags&CV_EVENT_FLAG_SHIFTKEY )

        {

                cout << "\tCV_EVENT_FLAG_SHIFTKEY" << endl;

        }

        if(flags&CV_EVENT_FLAG_ALTKEY )

        {

                cout << "\tCV_EVENT_FLAG_ALTKEY" << endl;

        }

}


void onMouse( int evt, int x, int y, int flags, void* param )

{

        SampleClass *p = (SampleClass *)param;

        p->HandleEvent(evt, x, y, flags);

}


void proc_video(VideoCapture *vc)

{

        SampleClass tmp;


        namedWindow("mousekeyboard");


        // event handler 등록

    setMouseCallback( "mousekeyboard", onMouse, &tmp );


        Mat frame;

        while(1)

        {

                *vc >> frame;

                if(frame.empty()) break;

                imshow("mousekeyboard", frame);

                char ch = waitKey(10);

                if( ch == 27 ) break;        // ESC Key

        }

}


int _tmain(int argc, _TCHAR* argv[])

{

        //select image source

        char data_src;

        cout << "1. camera input (640 x 480)\n"

                 << "2. camera input (320 x 240)\n"

                 << "3. video file input\n"

                 << endl

                 << "select video source[1-3]: ";

        cin >> data_src;


        VideoCapture *vc = NULL;

        if(data_src=='1')

        {

                //camera (vga)

                vc = new VideoCapture(0);

                if (!vc->isOpened())

                {

                        cout << "can't open camera" << endl;

                        return 0;

                }

                vc->set(CV_CAP_PROP_FRAME_WIDTH, 640);

                vc->set(CV_CAP_PROP_FRAME_HEIGHT, 480);

        }

        else if(data_src=='2')

        {

                //camera (qvga)

                vc = new VideoCapture(0);

                if (!vc->isOpened())

                {

                        cout << "can't open camera" << endl;

                        return 0;

                }

                vc->set(CV_CAP_PROP_FRAME_WIDTH, 320);

                vc->set(CV_CAP_PROP_FRAME_HEIGHT, 240);

        }

        else if(data_src=='3')

        {

                //video (avi)

                OPENFILENAME ofn;

                char szFile[MAX_PATH] = "";

                ZeroMemory(&ofn, sizeof(OPENFILENAME));

                ofn.lStructSize = sizeof(OPENFILENAME);

                ofn.hwndOwner = NULL;

                ofn.lpstrFile = szFile;

                ofn.nMaxFile = sizeof(szFile);

                ofn.lpstrFilter = _T("Avi Files(*.avi)\0*.avi\0All Files (*.*)\0*.*\0");

                ofn.nFilterIndex = 1;

                ofn.lpstrFileTitle = NULL;

                ofn.nMaxFileTitle = 0;

                ofn.lpstrInitialDir = NULL;

                ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;

                if(::GetOpenFileName(&ofn)==false) return 0;


                vc = new VideoCapture(ofn.lpstrFile);

                if (!vc->isOpened())

                {

                        cout << "can't open video file" << endl;

                        return 0;

                }

        }

        if(vc) proc_video(vc);


        if(vc) delete vc;


        destroyAllWindows();


        return 0;

}




onMouse는 마우스 이벤트를 처리하기 위한 callback 함수로서 함수명은 어떻게 지어도 관계없습니다. onMouse의 인자중 flags는 마우스 이벤트가 발생할 당시의 다른 마우스 버튼들의 상태 및 주요 키보드(shift, ctrl, alt) 상태를 알려줍니다. 마우스 이벤트를 사용하기 위해서는 setMouseCallback 함수를 사용해서 opencv의 namedWindow에 이벤트 핸들러를 등록해 주어야 합니다.


참고사항: 키보드만으로는 이벤트가 발생하지 않습니다. 마우스 이벤트가 발생할 때 부수적으로 주요 키보드 값을 참조할 수 있을 뿐입니다. 일반적인 키보드 이벤트도 같이 처리하고 싶으면 win32(or MFC) 키보드 메시지 처리를 해 주어야 합니다.


2. 예제코드 다운로드

OpenCVMouseEvent.cpp


이 예제코드에는 마우스 이벤트 처리 뿐만 아니라 opencv에서 카메라 연결하기, avi 파일 읽어들이는 예제를 포함하고 있습니다. 제가 간단한 opencv 프로그램을 짤 때 주로 사용하는 틀로서 다른 분들에게도 유용할 것으로 생각됩니다.


3. 예제코드 실행 예





by 다크 프로그래머


  • 문일현 2013.08.13 20:29 신고 ADDR 수정/삭제 답글

    이 자료도 감사히 사용하겠습니다.

  • 문일현 2013.09.04 10:36 신고 ADDR 수정/삭제 답글

    안녕하세요 질문이 있어 답글을 남김니다.

    마우스에서 발생하는 이벤트의 좌표는 확인을 하였는데 영상위에 Rect을 표시하거나 ROI를 설정을 할 때 어떻게 처리를 해줘야하는지 잘 몰라서 질문을 드립니다.

    • BlogIcon 다크pgmr 2013.09.04 10:54 신고 수정/삭제

      rect 그리는 건 cv::rect함수를 이용합니다만 opencv 관련 책을 참조하시는게 좋을듯 싶습니다. 영상에서 마우스로 roi를 설정하는 것은 프로그래밍적인 부분이라 어떻게 따로 설명드릴 성질이 아니라서 답변을 드리기 힘드네요.

    • 문일현 2013.09.04 11:48 신고 수정/삭제

      아 제가 궁금했던게 마우스 콜백함수에서 이벤트가 발생했을 때 좌표가 찍히는데 그 콜백함수에서 그 좌표를 proc_video 함수에서 어떻게 받아와야하나 싶어서 질문을 드렸습니다.

      전역변수로 rect를 선언해 놓고 받아와야 하는지 아니면 다른 방법이 있는지 몰라서...

      죄송합니다. 너무 쉬운 질문일 것 같은데 제가 프로그래밍을 잘하는 편이 아니라 배우는데 쉽지는 않네요.

    • BlogIcon 다크pgmr 2013.09.04 12:17 신고 수정/삭제

      위 예제코드의 SampleClass에 Rect를 멤버변수로 추가하면 어떨까 싶습니다.

  • 고민고민하지마 2014.07.29 13:33 신고 ADDR 수정/삭제 답글

    ;; 이 키워드로도 여길 방문할 수 있을줄은 생각도 못했네요
    좋은 자료 감사드립니다 ㅎ

  • darkjjang 2017.08.09 22:19 신고 ADDR 수정/삭제 답글

    //video (avi)
    OPENFILENAME ofn;
    char szFile[MAX_PATH] = "";
    ZeroMemory(&ofn, sizeof(OPENFILENAME));
    ofn.lStructSize = sizeof(OPENFILENAME);
    ofn.hwndOwner = NULL;
    ofn.lpstrFile = szFile;
    ofn.nMaxFile = sizeof(szFile);
    ofn.lpstrFilter = _T("Avi Files(*.avi)\0*.avi\0All Files (*.*)\0*.*\0");
    ofn.nFilterIndex = 1;
    ofn.lpstrFileTitle = NULL;
    ofn.nMaxFileTitle = 0;
    ofn.lpstrInitialDir = NULL;
    ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
    if (::GetOpenFileName(&ofn) == false) return 0;

    공유해주신 소스에서 제가 가지고 있는 영상에다가 적용하려고 하는데요. 그래서 3번 방법을 선택하면 되는것 같은데, 제가 잘라서 위에적은 소스에서 OPENFILENAME자리에 뭘넣어야되는 건가요..? 선언되지 않은 식별자라구 떠서요... 아주초보입니다. 예제 시험하듯이 공유하신거 써보고있어서요.. 그냥 제 동영상 FILE이름을 넣는다고 되는건 아닌거같구요..

    • BlogIcon 다크pgmr 2017.08.10 08:35 신고 수정/삭제

      OPENFILENAME은 원래 윈도우즈 라이브러리에 있는 구조체 이름입니다. windows.h를 include해야 하는데 (#include "windows.h"), 살펴보니 예제코드에 누락된 것 같습니다. 괜한 고생을 시켜드린 것 같아 죄송합니다. 본문에도 반영하였습니다.