Home [데브코스] 5주차 - OpenCV Keyboard and Mouse Event Processing
Post
Cancel

[데브코스] 5주차 - OpenCV Keyboard and Mouse Event Processing


키보드 이벤트 처리하기

키보드 입력 대기

1
int waitKey(int delay = 0);
  • delay : 밀리초 단위의 대기 시간, delay <= 0 이면 무한히 기다림
  • 반환값 : 눌린 키 값, 키가 눌리지 않으면 -1

참고 사항

  • waitkey() 함수는 OpenCV 창이 하나라도 있어야 정상 작동한다.

  • imshow() 함수 호출 후 waitkey() 함수를 호출해야 영상이 화면에 나타남

  • 주요 특수 키 코드 : ESC = 27, ENTER = 13, TAB = 9

  • 화살표키, 함수키 등의 특수키에 대한 이벤트 처리를 수행할 때는 waitkeyEx() 함수 사용


마우스 이벤트 처리하기

마우스 이벤트 처리를 위한 콜백 함수 등록

1
void setMouseCallback(const String& winname, MouseCallback onMouse, void* userdata = 0);

이를 호출하게 되면 사용자가 지정한 창에서 발생하는 mouse event를 사용자가 다시 처리할 수 있게 해준다.

  • winname : 창이름
  • onMouse : 마우스 콜백 함수 이름
    • 아래와 같은 형식의 함수를 정의하고, 해당 함수 이름을 지정하면 된다
      1
      
        typedef void (*MouseCallback)(int event, int x, int y, int flags, void* userdata);
      
    • event : 마우스 이벤트 종류, MouseEventTypes 상수 (더 자세한 것은 MouseEventTypes 정의를 보면 나온다)
    • x,y : 마우스 이벤트 발생 좌표
    • flags : 마우스 이벤트 플래그 ,MouseEventFlags 상수, 이벤트가 발생할 때의 상태를 나타내는 플래그, (더 자세한 것은 MouseEventFlags 정의를 보면 나온다)
    • userdata : setMouseCallback() 함수에서 지정한 값이 여기로 넘어오는 사용자 지정 데이터
  • userdata : 콜백 함수에 전달할 사용자 지정 데이터 (optional)


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void on_mouse(int event, int x, int y, int flags, void*);

int main(void) {

    Mat src = imread("lenna.bmp");

    namedWindow("src");
    setMouseCallback("src", on_mouse);

    imshow("src", src);

    /* 
    imshow("src", src); 
    setMouseCallback("src", on_mouse); */
}

void on_mouse(int event, int x, int y, int flags, void*) {
    cout << "on_mouse" << endl;
}

이처럼 setMouseCallback() 함수는 윈도우가 생성되어 있어야 작동하기 때문에 namedWindow를 통해 먼저 선언을 해주거나 imshow 뒤에 설정해두어야 한다. 그리고 3번째 인자로 void* 이 있어야 하지만, 디폴트값이 있어서 작성하지 않았다.

두번째 인자로는 마우스 콜백함수의 이름을 지정해준다. 이는 미리 선언해줘야 한다. 그래서 위에 작성해주고 정의 코드를 작성해준다. 5번째 인자도 필요하지 않으면 작성하지 않아도 된다.

마지막으로 정의 부분은 마우스가 창 위에 올라오는 순간 마우스를 인식해서 매번 on_mouse를 출력할 것이다.


그래서 특정 마우스 이벤트일 때만 출력할 수 있도록 수정을 해보고자 한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
void on_mouse(int event, int x, int y, int flags, void*) {
    switch (event) {
    case EVENT_LBUTTONDOWN: 
        cout << "EVENT_LBUTTONDOWN: " << x << ", " << y << endl;
        break;
    case EVENT_RBUTTONDOWN:
        cout << "EVENT_RBOTTONDOWN: " << x << ", " << y << endl;
    case EVENT_MOUSEMOVE:
        cout << "EVENT_MOUSEMOVE: " << x << ", " << y << endl;
    default:
        break;
    }
}

이렇게만 작성해도 되지만, 내가 마우스 이벤트를 발생시키는 순간 키보드나 마우스의 상태를 flags를 통해서 확인할 수 있다. 그래서 이를 if문에 사용하면 더 정밀한 출력을 만들 수 있다.

1
2
3
    case EVENT_MOUSEMOVE:
        if (flags == EVENT_FLAG_LBUTTON)
            cout << "EVENT_MOUSEMOVE: " << x << ", " << y << endl;

그리고 지난 번에 배웠던 circle을 추가하여 사용해본다.

1
2
3
4
5
6
7
    case EVENT_MOUSEMOVE:
        if (flags & EVENT_FLAG_RBUTTON) {
            circle(src, Point(x,y), 5, Scalar(200,200,0), 3, LINE_AA);
            // line(src, Point(x,y), 2 Scalar(0,255,255), -1, LINE_AA); // 누를 때마다 표기하여 그림그리기
            ptOld = Point(x, y); // 사용자가 빠르게 움직이면 끊기기 때문에, 이전에 위치한 위치를 기록한 후 이전 위치와 현재 위치 사이를 직선을 만들도록 하는 것이 중요하다.
			imshow("src", src);
        }

여기서 flags는 정의에 보면 정수로 표현되어 있다. 그렇기에 이를 == 이 아니라 &를 통해 설정되어 있는지를 확인해서 조건을 거는 것이 좋다. 왜냐하면 여기서 오른쪽 버튼을 누르면서 <ctrl>키를 함께 누를 경우 출력되지 않는 것을 볼 수 있다.

추가로 src라는 것은 메인 함수에 정의된 지역 변수다. 그렇다면 여기서 동작시키려면 에러가 나기 때문에 지역 변수를 전역 변수로 변환해줘야 한다. 따라서 main()안이 아닌 밖으로 Mat src; 를 추가하고, main()함수에서는 src = imread("lenna.bmp"); 라고 작성해야 한다.



트랙바 사용

트랙바(trackbar) : 영상 출력 창에 부착되어 프로그램 동작 중에 사용자가 지정도니 범위 안의 값을 선택할 수 있는 GUI, 슬라이더 컨트롤이다.

1
int createTrackbar(const String& trackbarname, const String& winname, int* value, int count, TrackbarCallback onChange = 0, void* userdata = 0);
  • trackbarname : 트랙바 이름, 바 옆에 나오는 이름(창 이름 아님)
  • winname : 트랙바를 생성할 창 이름
  • value : 트랙바 위치 값을 받을 정수형 변수의 주소, 포인터함수이므로 사용자가 트랙바를 움직일 때마다의 값을 저장
  • count : 트랙바 최대 위치 (최소 위치는 항상 0)
  • onChange : 트랙바 위치가 변경될 때마다 호출되게 만들 콜백 함수 이름(함수의 포인터)
    • 만약 NULL을 지정하면 콜백 함수는 호출되지 않고 Value로 지정한 변수값만 갱신됨
      1
      
        typedef void (*TrackbarCallback)(int pos, void* userdata);
      
  • userdata : 트랙바 콜백 함수에 전달할 사용자 데이터의 포인터 (optional)
  • 반환값 : 정상 동작하면 1, 실패하면 0

이 함수를 호출하기 전에 미리 창이 생성되어 있어야 한다.


  • 트랙바 예제
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <iostream>
#include "opencv2/opencv.hpp"

using namespace std;
using namespace cv;

void trackbarcallback(int pos, void* userdata);

int main(void)
{
	Mat img = Mat::zeros(600, 600, CV_8UC1);

	namedWindow("image");
	createTrackbar("level", "image", 0, 16, trackbarcallback, (void*)&img); // value가 0==NULL 값으로 할경우 반드시 콜백함수를 지정해야 한다. 
                                                                            // (void*)&img : img의 주소값을 전달하는데 이를 void* 타입으로 설정하는데 이는 trackbarcallback에 userdata로 전달이 된다.
	imshow("image", img);
	waitKey();
}

void trackbarcallback(int pos, void* userdata) // pos == 현재 트랙바 위치, userdata == (void*)&img
{
	Mat img = *(Mat*)userdata; // 받은 userdata를 img로 만듬, 

	img.setTo(pos * 16); // 받은 pos * 16을 곱한 값을 img 영상의 모든 픽셀을 설정, pos는 0~16사이의 정수값일 것이다. 255를 넘어가면 255로 설정되어 출력된다.
	imshow("image", img);
}
This post is licensed under CC BY 4.0 by the author.