티스토리 뷰

OpenCV

[Kinect with OpenCV] c로 구현하는 InfraRed

생각많은 소심남 2012. 11. 6. 20:39

10월 초에 Kinect for Windows SDK가 v1.6으로 업데이트되면서 생긴 포멧중의 하나가 InfraRed, 즉 적외선 모드였습니다. 그리고 Developer ToolKit에서도 이를 활용한 예제가 있었는데 그게 바로 Infrared Basic입니다.



그런데 ToolKit에 있는 모든 C++ 예제들이 그렇지만 역시 Direct2D로 구현되어 있기도 하고 이와 관련된 Reference가 잘되어 있지 않아서 조금 알아보기 힘듭니다. 

저도 조금 고민을 하다가 OpenCV로도 이걸 표현할 수 있지 않을까 해서 이것저것 해본 결과를 공유하고자 합니다.


우선 기본 세팅은 다른 포스트에서도 많이 언급했던 내용이니까 생략을 하고, 사전에 알아둘 점이 하나있습니다.

전 애초에 Color와 Depth, IR을 같이 뽑아내는 하나의 Kinect Explorer를 구현해보려고 했습니다. 그런데 만들면서 느낀 점은 IR모드에서 뿌려지는 출력이 Color를 기반으로 만들어지는 것이기 때문에 ColorStream과 같이 뿌릴 수가 없습니다. 이점만 알아두신다면 구현하는데 문제가 없을 듯 합니다.


자 우선 기본적인 헤더 파일을 추가해줍시다.



그리고 사전에 필요한 함수를 미리 정의해둡니다. 저는 크게 Kinect를 초기화하는 과정과 IRStream을 처리하는 함수, 그리고 IRStream을 색상으로 바꿔주는 함수에 대해서 정의를 했습니다. 그런데 사실 보시면 아시겠지만 이전의 Color나 Depth를 구현할 때와 많이 비슷합니다.



RGBQUAD야 지난번 Depth 처리할 때도 사용했던 자료형이고 거진 과정이 비슷합니다. 이 부분에 대해서는 뒤에서 마저 설명하기로 하겠습니다.

이제 main으로 들어가봅시다. 처음으로 해줄 과정은 이벤트 생성입니다.



OpenCV를 하면서 계속 나오는 것이지만 키넥트로부터 프레임을 받아오는 것 자체가 키넥트를 계속 구동하면서 이뤄지는 것이기 때문에 하나의 이벤트 핸들러를 이용해서 처리하게 됩니다. 물론 다른 이벤트들도 같이 사용하고 싶으시면 그냥 같이 써주시면 되겠지요. 지난번 Depth출력 때에도 이런 식으로 Color와 Depth를 같이 사용했었습니다. 

 다음으로 해줄것은 키넥트를 초기화하고 IR 이미지를 담을 자료형과 창을 생성해주는 과정입니다. 키넥트 초기화는 앞에서 생성한 InitializeKinect 함수를 사용할 겁니다.




여기서 유의할 점은 IR 이미지를 담는 InfraRed의 RGB 채널이 4로 되어있다는 겁니다. 문득 앞의 이미지만 보면 검정색과 흰색만 나오는데 그러면 1채널이 맞는게 아니냐고 하실 수도 있는데 1채널로 하면 이상한 이미지가 출력됩니다. 사실 IR 자체가 컬러기반에서 IR에 반사되는 것만 비추고 어떻게 보면 색이 조금 들어가긴 하지요.

 다음으로 해줄 과정은 NuiImageStreamOpen 함수를 통해서 어떤 이미지를 출력할 지를 결정하는 건데 당연히 IR을 뽑는 과정이니 그거에 맞춰서 세팅해줍니다.



참고로 이번에 새롭게 등장한 값 중하나가 바로 밑줄친 INFRARED이고 저걸 사용해야 위와 같은 이미지를 얻을 수 있습니다. 앞에서 HRESULT형인 hr을 선언한 이유는 지금과 같은 함수들의 Return 값을 통해서 동작이 정상적으로 이뤄지는지를 확인하기 위한 수단이라고 보시면 좋을거 같습니다. 기본적인 세팅은 여기까지 입니다. 이제 무한 루프내에서 해당 이벤트를 열심히 돌려주면 됩니다.



그에 대한 함수로 앞에서 미리 선언한 createIRImage를 사용하고 해당 이벤트를 넘겨주는 과정을 거치게 됩니다. 당연히 그만 받을 때의 경우도 생각해줘야 되기에 cvWaitKet를 사용해서 특정키 입력시 종료과 되는 형태를 취합니다. 위에서 0x001b는 esc버튼입니다. 물론 이미지를 뿌려주는 과정은 createIRImage에서 진행될 것이고요.

무한 루프에서 빠져나오면 지금까지 만든 이미지 정보를 전부 Release시켜줘야 합니다. 그리고 열린 창은 다 닫아줘야 하겠지요.


지난번에도 말씀드린 이야기지만 createIRImage에서 IplImage에 대한 Release가 이뤄지기 때문에 메인구문에서는 거기를 가리키는 Header만 없애주면 되겠습니다.

여기까지가 메인 구문에 대한 설명이고 이제 앞에서 정의한 함수 3개에 대한 구현이 필요합니다. 맨처음은 InitializeKinect()인데 매번 나오는 내용이니 그냥 코드만 보여드립니다.

  


이전코드를 재사용하는 과정에서 NuiInitialize에 여러개의 flag를 설정해줬지만 지금과 같이 IRStream만 사용할 경우에는 그냥 Color flag만 지정해주면 됩니다.

자 이제 남은게 createIRImage()와 Nui_ShortToQuad_IR인데 코드 진행이 Depth를 구현할 때와 거의 똑같습니다. 다만 조금 수정해준 점이라면 Nui_ShortToQuad_IR()에서 rgb값을 표현해줄 때 그냥 8bit을 shift 해주는게 Depth 구현때와 다른 점입니다. 그때는 PlayerIndex냐 DepthRange를 구분하냐에 따라서 shift 해주는 bit수가 조금 달랐었지요.

 


어떤가요? 거의 똑같습니다. 조금 차이를 둔게 Depth_Height이나 Width를 Color_width/height으로 바꿔준건데 사실 앞에서도 보셨겠지만 둘다 640*480을 취하고 있습니다.

자 이제 받은 데이터 값을 rgb로 바꿔주는 함수인데 앞에서 말씀드린데로 8bit을 shift해주는 과정만 더해진거 빼고는 별반 다를게 없습니다.



이게 끝입니다. 한번 결과를 보겠습니다.


잘 나오고 있지요. 사실 이 IR기능을 제공하는 이유 중 하나가 바로 빛의 간섭을 받지않기 때문에 그에 해당하는 환경에서도 유용하게 쓸 수 있다는 점입니다. 가령 빛이 적은 경우에는 RGB로 처리하기 어렵겠지만 위와 같이 IR을 사용하게 되면 그런걸 고려할 필요가 없을테지요.


물론 Depth와도 같이 써먹을 수 있습니다.



참고로 왼쪽에 있는 Depth는 Range를 조절해서 일정 범위내에서만 Depth를 표현하게끔 했습니다.

그러고보니까 OpenCV를 다루면서 Kinect로 할 수 있는 내용을 거진 해본거 같네요. Color나 Depth, Skeleton. 그리고 오늘해본 Infrared까지 말입니다. 이제 마지막으로 남은게 Audio에 관한 내용인데 이부분도 조금 공부를 해보고 다시 알려드릴 수 있는 기회가 생기면 좋겠습니다.

댓글