[OpenCV 팁] 이미지 창을 여러개 띄우면서 키보드 메시지 처리하기

프로그래밍/opencv 2015. 8. 15. 23:08

흔히 발생하는 상황은 아니지만 혹시 필요한 분도 있을 것 같아서 적어봅니다.


opencv에서는 imshow로 이미지를 띄운 후에는 반드시 waitKey를 호출해 주어야만 정상적으로 이미지가 보여집니다.


그런데, 단순히 이미지를 디스플레이하기 위한 목적으로만 waitKey를 사용하는 경우에는 관계가 없지만, 키보드 메시지도 같이 처리하기 위해 waitKey를 사용할 경우에는 출력할 이미지가 여러 개일 경우 조금 미묘한 문제가 발생합니다.


일단, 제 경우에는 아래와 같이 알고리즘을 테스트하는 중간 중간에 스페이스 키를 이용해서 프로그램을 pause시키고 또 esc 키를 이용해서 프로그램을 종료하는 형태를 즐겨 사용하는 편입니다.


VideoCapture vc(0);

Mat frame;

while(1)

{

vc >> frame;

...

imshow("result", frame);

char ch = waitKey(10);

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

if(ch == 32)                // 32 == SPACE key

{

while((ch = waitKey(10)) != 32 && ch != 27);

if(ch == 27) break;

}

}


그런데, 디스플레이하고 싶은 이미지가 2개 이상일 경우에 아래와 같이 코드를 작성하는 것은 키보드 메시지 처리가 정상적으로 동작하지 않는 문제가 발생합니다 (첫 번째 waitKey를 실행하는 도중에 키보드가 눌려지게 되면 그 키보드 메시지는 그대로 날아가기 때문에 프로그램에 반영이 되지 못함). 즉, 어떤 때는 키보드가 먹히고 어떤 때는 키보드가 먹히지 않는 현상이 나타납니다.


imshow("result1", frame1);

waitKey(10);

...

imshow("result2", frame2);

char ch = waitKey(10);

if(ch == 27) break;

...


그렇다고 아래처럼 waitKey를 한번만 호출하면 이번엔 이미지 출력에 문제가 발생합니다. 키보드는 잘 먹히지만 imshow마다 waitKey가 호출되지 않았기 때문에 일부 이미지들이 정상적으로 갱신되지 않는 현상이 나타납니다. 


imshow("result1", frame1);

imshow("result2", frame2);

char ch = waitKey(10);

...


이 문제로 종종 불편함을 겪다가 최근에 해결책을 하나 찾았는데 그것은 아래와 같이 매 waitKey 호출마다 키 값을 따로 저장해 두었다가 나중에 한꺼번에 처리하는 것입니다.


char key = -1;

imshow("result1", frame1);

char ch = waitKey(10); if(ch != -1) key = ch;

...

imshow("result2", frame2);

char ch = waitKey(10); if(ch != -1) key = ch;

...

if(key == 27) break;

if(key == 32) ...


그리고 이 방법을 좀더 확장하여, 만일 여러 함수 또는 파일에 걸쳐서 imshow가 필요한 경우에는 아래와 같이 imshow에 대한 wrapper 함수를 하나 만드는 것도 방법입니다.


class ShowImage

{

public:

    static void show(const char* winname, const cv::Mat& img, int delay = 1);

    static char getKey();


protected:

    static char key;

};


char ShowImage::key = -1;


void ShowImage::show(const char* winname, const cv::Mat& img, int delay)

{

    imshow(winname, img);

    char ch = waitKey(delay);

    if (ch != -1) key = ch;

}


char ShowImage::getKey()

{

    char ret = key;

    key = -1;

    return ret;

}


그리고 이미지 출력이 필요한 곳에서는 어디서나 ShowImage::show("result", frame)과 같은 식으로 이미지를 출력하고, 키보드 메시지가 필요한 곳에서는 char ch = ShowImage::getKey()를 호출하면 됩니다.


by 다크 프로그래머