티스토리 뷰

OpenCV

[Kinect with OpenCV] C++로 만들어보는 이미지 호출

생각많은 소심남 2012. 8. 6. 01:09

지난 포스트에서 간략하게 소개했다시피 간단하게 ColorStream을 뿌리는 방법에 대해서 언급해보고자 합니다. 

가장 간단하게 코드를 보는 방법은 바로 Kinect for Windows SDK v1.5의 Developer Toolkit에 들어있는 Color Basics Sample을 보면 됩니다. 해당 코드가 어디있는지 모르시는 분이라면 다음 버튼을 눌러서 설치하시면 됩니다.



자신이 원하는 경로에 설치하면 그에 해당하는 C++ 코드 프로젝트를 살펴볼 수 있습니다. 하지만 장담합니다만 보기가 참 어렵습니다. 그래서 다른 분이 만드신 코드를 활용해서 ColorStream을 OpenCV를 활용해서 내보내는 방법을 소개하고자 합니다. 당연한 말입니다만 이전의 준비과정은 다 하신 후에 진행하셔야 합니다.

출처: http://rucaus.egloos.com/2304178#344393.02


자 해당 dll이라 링커에 대한 설정이 진행되었으면 External Dependacies에서 헤더 파일을 읽어올겁니다. 우리가 필요한 OpenCV 라이브러리는 아주 기본적인 헤더파일인 cv와 highgui, cxcore.h 입니다. 먼저 그것들을 추가해줍시다. 다음은 WIN32 용 어플리케이션을 구성할 것이기 때문에 윈도우 헤더파일과 키넥트 헤더파일이 추가되어야 합니다. 마지막으로 윈도우 어플리케이션에서 돌리기 위한 변수를 이용하기 위한 헤더파일인 tchar.h도 추가시켜 줍니다. 마지막으로 기본적인 c++ 헤더파일들을 추가시켜주면 되겠지요.



마지막으로 자주쓰는 네임스페이스는 글로벌로 등록해주는게 좋겠지요.저는 출력 관련 함수를 이용하고 추가적으로 cv관련 함수를 쓸것이기 때문에 관련 네임스페이스를 추가시켜주었습니다. 참고로 저는 글로벌 변수를 둬서 출력시킬 창의 width와 height를 가져다가 쓸 수 있게끔 했습니다. 그래서 선언부에는 다음과 같이 구성되었습니다.



프로그램의 구성은 다음과 같이 구성될겁니다.

1. 키넥트를 초기화 시키는 과정

2. 키넥트 관련 프로그램 메모리 할당

3. 매초마다 ColorFrame을 창에 띄우는 과정


그러면 메인 구문은 다음과 같이 구성할 수 있겠지요.



지금이야 따로 헤더파일로 정의를 하지 않았기 때문에 어쩔 수 없이 메인 구문 앞에 InitializeKinect()에 대한 정의가 이뤄져야 합니다. 그 부분을 봅시다.



여기서 첫번째로 NuiApi.h에서 불러온 함수가 처음 등장하는데 바로 NuiInitialize() 입니다. 지난 시간에도 강조했었지만 msdn을 참고해 봅시다.

NuiInitialize는 자신이 원하는 기능에 대한 것을 플래그형식으로 인수로 집어넣는 형태를 취하고 있습니다. 



msdn에 따르면 사용할 수 있는 플래그는 총 5개정도로 정해져 있습니다. 물론 각각이 flag가 32bit형 constant로 정의되어 있기 때문에 여러 플래그를 동시에 사용할 수 있습니다. 당연히 그때는 우리가 잘 아는 OR 논리자를 사용하면 될겁니다. 일단 이번 예제는 Color만을 사용할 것이기 때문에 위처럼 정의가 된겁니다. 만약 여러개 기능을 사용하고 싶으면 다음과 같이 취하시면 됩니다.



그래서 위와 같이 하면 마치 c#의 try catch 구문처럼 해당 디바이스가 인식되었을 때만을 넘겨서 오류를 관찰하게 됩니다. 

계속해서 main 구문을 작성해봅니다.

 Opencv에서는 이미지형 변수가 포인터로 존재합니다. 컬러 색상은 4채널 RGBA로 넘어오고 그걸 띄울 창을 선언해줍니다.



이제 다음으로 등장하는 Kinect API가 바로 NuiImageStreamOpen입니다. 여기서는 아까 잠깐 나왔던 Flag와 해상도 및 앞에서 지정했던 이벤트들을 삽입하게 됩니다. 물론 앞에서 해줬던 것처럼 오류가 났을 때에 대한 오류 구문도 삽입해주면 좋겠지요.



이로써 앞에서 지정한 colorStreamHandle은 이 ColorFrame을 받는 이벤트로 동작하게 됩니다. 이제 실시간으로 프레임을 받아오기 위해서는 무한 루프를 돌려서 해당 이벤트에서 받아오는 프레임을 창에 뿌려주는 동작을 구현해주면 됩니다. 저는 그 코드 부를 CreateRGBImage라는 함수로 선언해줬습니다. 

 참고로 계속 무한 루프를 돌면 안되니까 키를 받아오게 해서 esc를 눌렀을 때 루프를 빠져나가게끔 했습니다. 아마 OpenCV에서 CAM에서 frame 받아오는 것을 해보신 분이라면 익숙한 구조일 것입니다.



마지막으로 키넥트가 종료될때는 반드시 NuiShutdown을 사용해서 기능을 꺼주게 해야 합니다. 이렇게 안하면 프로그램이 꺼졌음에도 키넥트가 계속 동작되고 있는 것을 확인할 수 있습니다. 



자 그럼 다시 위로 올라가서 createRGBImage를 정의해줘야 합니다. 

캠이란 것도 그렇지만 모든 동영상은 각각의 프레임으로 표현이 됩니다. 보통 사람이 자연스럽게 여기는 프레임속도는 30fps가 됩니다. 영화도 23fps정도가 되고요. 그래서 매 초마다 그 프레임에 대한 정보를 받아오는 과정을 해주는 것이 바로 createRGBImage입니다. 



우선은 frame을 받아서 넘겨줘야 하기때문에 그에 대한 자료형이 필요하고 Nuiapi.h에서는 그 자료형을 NUI_IMAGE_FRAME으로 정의해놓고 있습니다. 그래서 매초마다 다음 프레임에 대한 정보를 받기 위해 필요한 함수인 NuiImageSreamGetNextFrame()을 사용하고 있습니다. 또 이에 대한 오류 체크 부분이 필요하겠지요.



다음 부분입니다. 게임 구현을 해보신 분은 아시겠지만 받아온 Frame정보를 정해진 틀안에 뿌려주기 위해서는 그 프레임 정보를 하나의 Texture로 만드는 과정이 필요합니다. 이걸 LockedRect이라는 사각형에 넣어주는 것이 위 코드의 내용입니다. 그래서 최종적으로 시간 단위로 ColorImage라는 창에 컬러 프레임이 뿌려지게 됩니다. 

 마지막으로 이런 프레임이 누적되면 메모리 부족 현상이 발생하기 때문에 이걸 해제시켜주는 과정이 필요합니다. 그걸 해주는 것이 맨 마지막에 나와있는 NuiImageSreamReleaseFrame입니다. 


어떤가요? 계속 코드 내용만 말씀드려서 어려울 수도 있겠지만 원리는 딱하나만 이해하면 됩니다. 우리가 보는 영상은 프레임이 연속적으로 뿌려지는 과정이다. 그 프레임을 만들기 위해서 Kinect 헤더파일속의 몇몇 함수가 필요하다 정도 입니다.

결과는 다음과 같이 나옵니다.



여기서 ColorImage를 OpenCV에서 활용하고 싶다하면 앞에서 말한 CreateRGBImage 상의 Color로 정의되는 것이 있습니다.

이게 메인에서 불러오는 것이기 때문에 결론적으로는 메인 함수상에서 Color 를 활용하면 되는 것이지요. 

이걸 활용하면 컬러를 활용한 Canny edge나 contour detection 같은 기법도 적용할 수 있겠지요. 

DepthImage를 뽑는 과정도 어떻게 보면 비슷합니다.

그 부분은 다음 포스팅에서 소개하도록 하겠습니다.



댓글