본문 바로가기

프로그래밍 강좌/C++ - OpenCV

[OpenCV][C++] 윈도우 생성, 키보드 이벤트 처리 방법 총정리 - namedWindow imshow waitkey waitkeyEx setWindowProperty

지난번에 트랙바를 사용하는 방법에 대해 살펴보았습니다.

 

이번에는 키보드 마우스 이벤트 받고 처리하는 방법에 대해 상세히 알아볼께요.

키보드와 마우스 이벤트를 받기 위해서는 윈도우가 하나 필요하겠죠?

윈도우 생성

winName 이란 이름을 갖는 창을 생성하는 함수.

 
winName
창 이름 (생성할 창의 고유 이름으로 창의 캡션(타이틀)에 사용되는 이름임)
window
Flag
기본값: cv::WINDOW_NORMAL (resizeWindow 함수로 윈도우 크기 조절 가능)
cv::WINDOW_AUTOSIZE (영상 사이즈에 맞게 자동 조절됨, resizeWindow 사용 X)
cv::WINDOW_OPENGL (OpenGL을 지원하는 윈도우 생성)
cv::WINDOW_FULLSCREEN (창을 전체 화면으로 생성, AUTOSIZE와 같은 결과)
cv::WINDOW_FREERATIO (창의 가로/세로 비율을 마음대로 조절 가능하도록)
cv::WINDOW_KEEPRATIO (창의 가로/세로 비율 유지한채로 창 크기 조절)
cv::WINDOW_GUI_EXPANDED (상태바와 툴바 표현 가능)

 

OPENGL 플래그를 지정할 경우 imshow 함수에서 cv::ogl::Buffer, cv::ogl::Texture2D, cv::cuda::GpuMat 등을 입력으로 받을 수 있습니다.

cv::namedWindow("Lena_Image", cv::WINDOW_FREERATIO);
 

WINDOW_FREERATIO로 설정하여 가로/세로 비율을 마음껏 조절할 수 있습니다.

창의 모서리에 마우스를 가져가면 마우스의 모양이 크기 조절 할 수 있도록 바뀝니다.

창의 속성을 추가로 지정해줄 수 있습니다.

창을 맨 위로 놓아야 할 경우에 주로 사용되는 함수 입니다. (나머지 속성은 cv::namedWindow()에서도 설정 가능함

 
winName
창 이름
wndPropertyFlag
cv::WND_PROP_FULLSCREEN: FULLSCREEN 속성(Flag: NORMAL, FULLSCREEN)
cv::WND_PROP_AUTOSIZE: AUTOSIZE 속성(Flag: NORMAL, AUTOSIZE)
cv::WND_PROP_ASPECT_RATIO: 창의 가로/세로 비율, (Flag:FREERATIO, KEEPRATIO)
cv:WND_PROP_OPENGL: OPENGL 속성
cv::WND_PROP_VISIBLE: 창이 존재하고 표시되는 속성
cv::WND_PROP_TOPMOST: 창을 항상 맨 위로 표시
cv::WND_PROP_VSYNC: VSYNC 활성화 또는 비활성화(Flag: OPENGL)
windowFlag
위 표 참조.

 

cv::setWindowProperty("Lena_Image", cv::WND_PROP_TOPMOST, cv::WINDOW_AUTOSIZE);
 

창의 Property를 TOPMOST로 설정하여 항상 맨 위에 떠 있게 됩니다.

뒤의 창을 선택하였음에도 창은 맨 위에 떠 있습니다. (창의 타이틀 활성화 여부 확인)

창의 타이틀을 별도로 설정할 수 있음

별도 지정이 없으면 창 이름 타이틀이 됩니다.

다른 window 관련 함수의 창 이름에는 창 이름을 넣어주셔야 합니다. 타이틀은 별명입니다.

 
winName
창 이름
title
창의 타이틀 바에 표시될 string

위에서 계속 창 이름을 "Lena_Image"라고 하였고, 타이틀도 그렇게 지정이 되지만,

이번에는 타이틀을 변경해보겠습니다.

cv::setWindowTitle("Lena_Image", "src_gray");
 

OpenCV를 만지면서 가장 많이 불러본 이름일 꺼에요.

winName을 가진 창 영상을 표시하라는 의미입니다.

cv::namedWindow()가 없이 cv::imshow() 만 이용할 경우 windowFlag = cv::WINDOW_AUTOSIZE 인 윈도우 자동으로 생성합니다.

cv::imshow("Lena_Image", src);
 

생성된 창을 파괴(없애는)하는 함수 입니다.

destroyWindow() winName 을 가진 창만 파괴를 하고

destroyAllWindows() OpenCV에서 생성한 모든 창을 파괴합니다.

winName의 이름을 가진 창의 좌상단(left-top) 위치를 (x, y)로 이동시키는 함수입니다.

여러개 창을 띄울 때 각각 창의 위치를 항상 고정시킬 때 사용합니다.

창의 사이즈를 (width, height) 로 변경하는 함수입니다.

cv::namedWindow()에서 cv::WINDOW_AUTOSIZE 로 생성한 창에서는 크기를 재조정 할 수 없습니다.

AUTOSIZE 에서 만약 영상보다 큰 사이즈로 재조정을 하면 아래와 같이 창의 크기는 재조정 되지만 영상의 크기는 원본 그대로 표시가 됩니다.

cv::resizeWindow("Lena_Image", 800, 600);
 

위에서 사용한 전체 소스는 아래와 같습니다.

#include <iostream>
#include "opencv2/opencv.hpp"

int main()
{
	cv::Mat src = cv::imread("lena_gray.bmp", cv::IMREAD_GRAYSCALE);
	if (src.empty())  return 1;
	
	cv::namedWindow("Lena_Image", cv::WINDOW_AUTOSIZE);
	cv::setWindowProperty("Lena_Image", cv::WND_PROP_TOPMOST, cv::WINDOW_AUTOSIZE);
	cv::setWindowTitle("Lena_Image", "src_gray");
	cv::imshow("Lena_Image", src);
	cv::moveWindow("Lena_Image", 10, 10);
	cv::waitKey();

	cv::resizeWindow("Lena_Image", 800, 600);
	cv::waitKey();
	cv::destroyAllWindows();	

	return 0;
}
 
키보드 이벤트 처리

키보드 이벤트를 받는 함수는 아래와 같습니다.

delay
기본값: 0, delay ≤ 0 이면 무한히 기다립니다.
반환값
눌려진 키 값, ASCII 코드 값, 만약 지정된 시간동안 키 입력이 없으면 -1 반환함

 

만약 esc 키를 눌렀을 경우 창을 파괴하고 싶다면 아래와 같이 사용하시면 됩니다.

여기에서 27 값은 esc 키에 해당하는 ASCII 코드 값 입니다.

출처:  http://www.theasciicode.com.ar/

 

if (cv::waitKey() == 27) {
	cv::destroyWindow("Lena_Image");
}
 

cv::waitKey()는 함수 키(F1, F2, ...) 또는 화살표 키(→, ←, ↑, ↓) 등 특수 키 입력은 처리를 못합니다. 위의 아스키 코드에 없죠?

이런 키를 입력받고 싶다면 cv::waitKeyEx()를 사용하면 됩니다.

화살표 키를 입력 받으면 화면의 동그란 원이 움직이는 소스를 한번 만들어 볼께요.

 

1. 배경 영상 생성

500 x 500 사이즈 black 영상을 생성합니다.

int width = 500;
int height = 500;
cv::Mat src = cv::Mat::zeros(cv::Size(width, height), CV_8UC1);
 

2. 창의 이름 지정

창의 이름을 "Test"라고 지정할께요.

cv::namedWindow("Test");
 

3. 중앙에 원을 그리고 영상을 보여준다

cv::circle을 이용하여 영상의 중앙에 동그란 원을 하나 그린다.

int x = width/2;
int y = height/2;
cv::circle(src, cv::Point(x, y), 5, cv::Scalar(255), cv::FILLED);
cv::imshow("Test", src);
 

4. 키보드 입력을 기다린다.

특수키가 포함된 키보드 입력을 기다린다.

좌표가 입력되면 기존 동그란 원은 지운다. (검정색 원을 그린다.)

int key = cv::waitKeyEx();
cv::circle(src, cv::Point(x, y), 5, cv::Scalar(0), cv::FILLED);
 

5. 키가 입력되면 좌표를 갱신한다.

switch (key) {
case 0x250000: // left 화살표
	if (x > offset) 	        x -= offset;
	break;
case 0x260000: // up 화살표
	if (y > offset) 	        y -= offset;
	break;
case 0x270000: // right 화살표
	if (x < width - offset) 	x += offset;
	break;
case 0x280000: // down 화살표
	if (y < height - offset) 	y += offset;
	break;
case 0x1B:		// esc 키
	bIsContinue = false;
	break;
}
 

특수키는 아래와 같습니다.

(디버그 모드로 직접 확인하셔도 됩니다. 십진수 또는 16진수로 나타낼 수 있습니다.)

page up 키
0x210000
F1
0x700000
page down 키
0x220000
F2
0x710000
end 키
0x230000
F3
0x720000
home 키
0x240000
F4
0x730000
← 화살표
0x250000
F5
0x740000
↑ 화살표
0x260000
F6
0x750000
→ 화살표
0x270000
F7
0x760000
↓ 화살표
0x280000
F8
0x770000
insert 키
0x2d0000
F9
0x780000
del 키
0x2e0000
F10
0x790000


F11
0x7a0000


F12
0x7b0000

 

6. 갱신된 좌표 그림을 그리고 화면에 표시합니다.

cv::circle(src, cv::Point(x, y), 5, cv::Scalar(255), cv::FILLED);
cv::imshow("Test", src);
 

전체 소스는 아래와 같습니다.

#include <iostream>
#include "opencv2/opencv.hpp"

int main()
{
	int width = 500;
	int height = 500;
	int offset = 10;
	cv::Mat src = cv::Mat::zeros(cv::Size(width, height), CV_8UC1);
	
	cv::namedWindow("Test");
	
	int x = width/2;
	int y = height/2;
	cv::circle(src, cv::Point(x, y), 5, cv::Scalar(255), cv::FILLED);
	cv::imshow("Test", src);

	bool bIsContinue = true;
	while (bIsContinue) {
		int key = cv::waitKeyEx();
		cv::circle(src, cv::Point(x, y), 5, cv::Scalar(0), cv::FILLED);
		switch (key) {
		case 0x250000: // left 화살표
			if (x > offset) {
				x -= offset;
			}
			break;
		case 0x260000: // up 화살표
			if (y > offset) {
				y -= offset;
			}
			break;
		case 0x270000: // right 화살표
			if (x < width - offset) {
				x += offset;
			}
			break;
		case 0x280000: // down 화살표
			if (y < height - offset) {
				y += offset;
			}
			break;
		case 0x1B:		// esc 키
			bIsContinue = false;
			break;
		}
		cv::circle(src, cv::Point(x, y), 5, cv::Scalar(255), cv::FILLED);
		cv::imshow("Test", src);
	}		
	
	return 0;
}