OpenCV 마우스 이벤트 처리하기

프로그래밍/opencv 2013. 2. 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. 2. 13. 16:47

실시간(라이브)으로 왜곡 보정된 영상을 보여주는 프로그램입니다.

왜곡보정에 관한 이론적인 내용은 [영상처리] - 카메라 왜곡보정 - 이론 및 실제을 참조해 주세요.

 

 

 

프로그램 다운로드 (소스코드 & 샘플동영상 포함)

DistortCorrection.zip
다운로드

☞ 포함된 소스코드는 왜곡보정에 관련된 핵심 코드만 포함되어 있습니다. 사용자 인터페이스 및 파일 입출력 등 전체 프로그램 코드는 포함되어 있지 않습니다.

 

사용법

 

1. 압축을 풀면 아래 그림과 같이 camera_parameters.txt란 파일이 있는데, 먼저 이 파일을 메모장으로 열어서 카메라 파라미터를 이곳에 적어줍니다. 카메라 캘리브레이션은 [영상처리] - 카메라 캘리브레이션 (Camera Calibration)을 참조하세요.

기본값으로 설정되어 있는 파라미터값은 첨부된 sample.avi 동영상 파일을 획득하는데 사용된 카메라의 파라미터 값입니다.

 

 

 

2. 다음으로 DistortCorrection.exe를 실행시키면 다음과 같이 비디오 소스를 선택할 수 있습니다. 컴퓨터에 연결된 웹캠(webcam)을 사용하려면 1 또는 2를 입력하고, avi 파일을 읽어오려면 3을 입력하고 enter키를 누릅니다.

 

 

 

3. 그러면 아래 그림과 같이 실시간으로 왜곡 보정된 영상을 보여줍니다. 플레이 도중에 SPACE 키를 누르면 동적으로 왜곡 보정 기능을 켜고 끌 수 있습니다.

 

 

by 다크 프로그래머

'개발한 것들' 카테고리의 다른 글

걷는 속도와 비를 맞는 양 - 컴퓨터 시뮬레이션  (54) 2013.06.05
FFT와 모아레 제거 프로그램  (76) 2013.01.28
오목  (52) 2013.01.28

OpenCV 편하게 사용하기 (팁)

프로그래밍/opencv 2013. 2. 13. 12:18

1. OpenCV 링크 편하게 하기

2. OpenCV 행렬 원소접근 편하게 하기

3. TypedMat 사용법




1. OpenCV 링크 편하게 하기


OpenCV를 이용한 프로그램을 작성하려면 필요한 헤더화일 및 라이브러리 파일들을 링크시키기 위해 이런 저런 세팅을 해 주어야 합니다.


저는 OpenCV 헤더파일 및 라이브러리 파일들을 다음과 같이 하나의 파일에 모두 넣어놓고 사용합니다 (물론 프로젝트 설정에서 opencv include directory, opencv library directory 는 별도로 설정해 주어야 합니다).


이제 opencv를 사용하고 싶으면 아래 파일만 include하면 ok입니다.


<use_opencv.h>

#ifndef __USE_OPENCV_H__

#define __USE_OPENCV_H__


#include "opencv2/highgui/highgui.hpp"

#include "opencv2/imgproc/imgproc.hpp"

#include "opencv2/core/core.hpp"

#include "opencv2/core/core_c.h"

#include "opencv2/highgui/highgui_c.h"

#include "opencv2/imgproc/imgproc_c.h"

#include "opencv2/video/video.hpp"

#include "opencv2/nonfree/nonfree.hpp"

#include "opencv2/videostab/videostab.hpp"

#include "opencv2/features2d/features2d.hpp"

#include "opencv2/objdetect/objdetect.hpp"

#include "opencv2/flann/miniflann.hpp"

#include "opencv2/photo/photo.hpp"

#include "opencv2/calib3d/calib3d.hpp"

#include "opencv2/ml/ml.hpp"

#include "opencv2/contrib/contrib.hpp"

#include "opencv2/ts/ts.hpp"

#include "opencv2/stitching/stitcher.hpp"

#include "opencv2/legacy/legacy.hpp"


#ifdef _DEBUG

      #pragma comment(lib,"opencv_core241d.lib")

      #pragma comment(lib,"opencv_highgui241d.lib")

      #pragma comment(lib,"opencv_imgproc241d.lib")

      #pragma comment(lib,"opencv_video241d.lib")

      #pragma comment(lib,"opencv_nonfree241d.lib")

      #pragma comment(lib,"opencv_videostab241d.lib")

      #pragma comment(lib,"opencv_features2d241d.lib")

      #pragma comment(lib,"opencv_objdetect241d.lib")

      #pragma comment(lib,"opencv_flann241d.lib")

      #pragma comment(lib,"opencv_photo241d.lib")

      #pragma comment(lib,"opencv_calib3d241d.lib")

      #pragma comment(lib,"opencv_ml241d.lib")

      #pragma comment(lib,"opencv_contrib241d.lib")

      #pragma comment(lib,"opencv_ts241d.lib")

      #pragma comment(lib,"opencv_stitching241d.lib")

      #pragma comment(lib,"opencv_legacy241d.lib")

#else

      #pragma comment(lib,"opencv_core241.lib")

      #pragma comment(lib,"opencv_highgui241.lib")

      #pragma comment(lib,"opencv_imgproc241.lib")

      #pragma comment(lib,"opencv_video241.lib")

      #pragma comment(lib,"opencv_nonfree241.lib")

      #pragma comment(lib,"opencv_videostab241.lib")

      #pragma comment(lib,"opencv_features2d241.lib")

      #pragma comment(lib,"opencv_objdetect241.lib")

      #pragma comment(lib,"opencv_flann241.lib")

      #pragma comment(lib,"opencv_photo241.lib")

      #pragma comment(lib,"opencv_calib3d241.lib")

      #pragma comment(lib,"opencv_ml241.lib")

      #pragma comment(lib,"opencv_contrib241.lib")

      #pragma comment(lib,"opencv_ts241.lib")

      #pragma comment(lib,"opencv_stitching241.lib")

      #pragma comment(lib,"opencv_legacy241.lib")

#endif  // _DEBUG


#endif  // __USE_OPENCV_H__



2. OpenCV 행렬 원소접근 편하게 하기


저는 opencv를 사용하면서 가장 불편한 점 중의 하나가 행렬(Mat)의 원소를 접근하는 것입니다. 특히나 영상처리를 하다보면 이미지 픽셀 단위로 처리를 할 필요가 있는데 OpenCV가 제공하는 원소 접근 방법(at)은 상당히 불편합니다.


그래서 아래와 같이 Wrapper class를 하나 작성해 보았습니다.


template<class T>

class TypedMat

{

        T** m_pData;

        int m_nChannels;

        int m_nRows, m_nCols;


public:

        TypedMat():m_pData(NULL),m_nChannels(1),m_nRows(0),m_nCols(0){}

        ~TypedMat(){if(m_pData) delete [] m_pData;}


        // OpenCV Mat 연동 (메모리 공유)

        void Attach(const cv::Mat& m);

        void Attach(const IplImage& m);

        TypedMat(const cv::Mat& m):m_pData(NULL),m_nChannels(1),m_nRows(0),m_nCols(0) { Attach(m);}

        TypedMat(const IplImage& m):m_pData(NULL),m_nChannels(1),m_nRows(0),m_nCols(0) { Attach(m);}

        const TypedMat & operator =(const cv::Mat& m){ Attach(m); return *this;}

        const TypedMat & operator =(const IplImage& m){ Attach(m); return *this;}


        // 행(row) 반환

        T* GetPtr(int r)

        { assert(r>=0 && r<m_nRows); return m_pData[r];}


        // 연산자 중첩 (원소접근) -- 2D

        T * operator [](int r)

        { assert(r>=0 && r<m_nRows); return m_pData[r];}


        const T * operator [](int r) const

        { assert(r>=0 && r<m_nRows); return m_pData[r];}


        // 연산자 중첩 (원소접근) -- 3D

        T & operator ()(int r, int c, int k)

        { assert(r>=0 && r<m_nRows && c>=0 && c<m_nCols && k>=0 && k<m_nChannels); return m_pData[r][c*m_nChannels+k];}


        const T operator ()(int r, int c, int k) const

        { assert(r>=0 && r<m_nRows && c>=0 && c<m_nCols && k>=0 && k<m_nChannels); return m_pData[r][c*m_nChannels+k];}

};


template<class T>

void TypedMat<T>::Attach(const cv::Mat& m)

{

        assert(sizeof(T)==m.elemSize1());


        m_nChannels = m.channels();

        m_nRows = m.rows;

        m_nCols = m.cols;

        

        if(m_pData) delete [] m_pData;

        m_pData = new T * [m_nRows];

        for(int r=0; r<m_nRows; r++)

        {

                m_pData[r] = (T *)(m.data + r*m.step);

        }

}


template<class T>

void TypedMat<T>::Attach(const IplImage& m)

{

        assert(sizeof(T) == m.widthStep/(m.width*m.nChannels));


        m_nChannels = m.nChannels;

        m_nRows = m.height;

        m_nCols = m.width;

        

        if(m_pData) delete [] m_pData;

        m_pData = new T * [m_nRows];

        for(int r=0; r<m_nRows; r++)

        {

                m_pData[r] = (T *)(m.imageData + r*m.widthStep);

        }

}



use_opencv.h



3. TypedMat 사용법


메모리를 공유하기 때문에 추가적인 연산 로드는 거의 없습니다. IplImage, cv::Mat을 지원합니다. 사용법은 다음과 같습니다.


* 이미지의 경우

#include "use_opencv.h"


Mat image;


TypedMat<unsigned char> tm = image;    // 연결방법 1

TypedMat<unsigned char> tm; tm = image;    // 연결방법 2

TypedMat<unsigned char> tm; tm.Attach(image);    // 연결방법 3


// image가 1채널 grayscale 이미지일 경우

tm[y][x] = 100;    // (x,y)의 픽셀값을 100으로 설정


// image가 3채널 color 이미지일 경우

tm(y,x,0) = 100;    // (x,y)의 픽셀의 blue값을 100으로 설정

tm(y,x,1) = 200;    // (x,y)의 픽셀의 green값을 200으로 설정

tm(y,x,2) = 50;    // (x,y)의 픽셀의 red값을 50으로 설정


* 2D float 타입 행렬의 경우

#include "use_opencv.h"

Mat f_image;

TypedMat<float> fm = f_image;

fm[y][x] = 3.12f;


by 다크 프로그래머