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

프로그래밍/opencv 2015.08.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 다크 프로그래머


  • 천하제일 2015.09.29 01:02 신고 ADDR 수정/삭제 답글

    대단하십니다 이해를 기초로 한 수학이해라 아 대단하십니다

  • BlogIcon 1466062071 2016.06.16 16:27 신고 ADDR 수정/삭제 답글

    좋은 정보 잘보고 갑니다

  • 라도파 2016.07.12 13:10 신고 ADDR 수정/삭제 답글

    관심이 있어서 찾아보니 이게 마지막 포스팅이시네요 ㅎㅎ 댓글관리만 해도 바쁘실 것 같습니다.
    이 글 보니까 생각나는데, 시간되시면 OpenCV + VisualStudio 전용 디버거 소개해줘도 괜찮을 것 같네요. http://opencv.org/image-debugger-plug-in-for-visual-studio.html 최대로 확대하면 리눅스에서 보는 것처럼 픽셀 위에 값이 나와요. 이미지는 imshow같은 걸로 띄운다고 해도, 일반 매트릭스는 값 확인하기가 힘들었는데 정말 좋네요.

    • BlogIcon 다크pgmr 2016.07.13 07:27 신고 수정/삭제

      네 그런 정보도 무척 유용할 것 같습니다. 참고토록 하겠습니다. (저는 ImageWatch라는 extension을 사용하는데 아마도 비슷한 기능으로 생각됩니다. 그리고 Visual C++ Refactoring이라는 extension도 써보니 유용한 것 같습니다)

  • 입원중 2016.08.09 19:05 신고 ADDR 수정/삭제 답글

    마지막 줄에 오타가 있네요:)
    ShowImage::geyKey() -> ShowImage::getKey()

  • BlogIcon DRAGONITE 2017.09.01 21:43 신고 ADDR 수정/삭제 답글

    안녕하세요!! 좋은 글 감사합니다 ^^ 이해 한 것이 맞는지 궁금합니다.. 첫번째 예제문에서 space 키를 누르게 되면 다른 키보드 입력을 받기 전까지는 ch = 32 로 남아있는 것인가요? 그리고 if(ch==32)로 들어가게 되서 while 문에 갇혀서 새로운 frame 을 받지 못하여 정지된 화면 처럼 보이는 것이 맞나요??

  • tosk3927 2018.01.26 21:02 신고 ADDR 수정/삭제 답글

    안녕하세요! 늘 좋은글 감사합니다!
    visual 에서는 여러 이미지 창을 띄워 놓고 마지막에 waitkey를 써도 괜찮았는데, xcode 사용하면서 안되서 해당 글을 참고하여 고쳤습니다. 그런데 모든 영상 각각 delay가 있다보니 이미지가 각각 다르게 움직입니다ㅠㅠ 혹시 delay 는 어떻게 주셨나요??

    • BlogIcon 다크pgmr 2018.01.26 22:00 신고 수정/삭제

      delay는 1로 주었습니다. 만일 의도적으로 delay가 필요하다면 마지막 show에서만 delay를 크게 주면 될 것 같습니다만..