티스토리 뷰

Kinect

[Kinect 프로젝트] Sensing a Body

생각많은 소심남 2012. 8. 7. 00:46

MS에서는 학생을 가르치는 교수들에게 무료로 관련 교육자료를 제공하는 제도가 있습니다. 그중 하나가 바로 Faculty Resource Center인데요. 여기에는 여러가지 Technology에 관한 교육자료가 망라되어 있습니다. 

 당연히 이번 파트에서 소개해 드릴 내용인 키넥트에 관한 내용도 있는데요. 장시간에 걸쳐서 키넥트를 처음 다루는 사람에게 최대한 쉽게 알려드리고자 합니다. 구성언어는 C#이 될 것이고, 유기적인 활동을 위해서 게임엔진인 XNA Framework를 이용할 겁니다. 당연히 프로젝트는 XNA 프로젝트로 생성되어야 하겠지요.

 이번 포스트에서 이야기 드릴 내용은 키넥트를 처음 접하는 학생을 위한 골격인식 방법입니다. 코드 구성도 쉽기 때문에 누군가에게 알려줄 교육자료가 필요하신 분이라면 참고하시면 되겠습니다. 이번부터 주기적으로 하나씩 올리겠습니다.


우선 C# XNA 프로젝트로 하나 생성합니다. 평소에는 WPF로 프로젝트를 구성했지만 이번에는 XNA 프로젝트입니다. 탬플릿중에 XNA Game Studio가 있는데 거기서 선택하시면 됩니다.



참고로 맨위의 Windows Phone용이 아닌 Windows Game 용으로 생성해야 합니다. 안그럼 디버깅시 폰으로 에뮬레이팅되는 현상이 발생합니다. 자 키넥트 기능을 활용하기 위해서는 Reference에서 Microsoft.Kinect 파일을 추가시켜야 합니다. Project Solution View 상에서 Reference를 선택하고 Add Reference를 통해서 추가시켜줍니다.



당연히 코드상에서 해당 메서드를 적용시키려면 네임 스페이스로도 추가해줘야 합니다.



이제 본격적인 코드 작성입니다. 우리가 이번 프로젝트를 통해서 확인하려고 하는건 키넥트의 골격 인식 기능이 정상적으로 작동하는지 입니다. 이를 위해서는 키넥트의 기능을 활성화시키기 위한 자료형과 골격 정보를 넣기 위한 자료형을 생성해줘야 합니다. 메인 클래스안의 상단에 전역변수로 넣어줍시다. 그리고 골격이 몇개 인식이 되었는지를 보여주려고 합니다. 그에 대한 변수는 정수형으로 하나 넣어줍니다.



이 XNA로 생성한 프로젝트는 기본적인 게임 프로젝트의 형태를 지닙니다. 보통 게임 프로그래밍을 해보신 분이라면 아시겠지만 전체적인 구조가 다음 과 같이 구성됩니다.

Initialize - LoadContent - Update - draw

일반적인 WPF라면 코드상에서 그려주고 해당 이미지를 호출하는 역할이 순차적으로 진행되지만 XNA에서는 사전에 컨텐츠를 불러와서 필요할 때 게임 동작과 동시에 호출시켜줍니다. 따라서 전체적인 동작이 빠릅니다. 그리고 블록화가 되어 있어서 개발자가 코드를 작성하기에 편리합니다. 예를 들어 뭔가 화면속에 이미지를 호출해와야 되는 거는 draw에서 관련 코드를 작성시켜주면 됩니다. 적이 랜덤하게 등장한다는 내용은 Update상에 넣어줄 수도 있고요. 정말 무궁무진한 겁니다.

 자 그럼 일단 변수를 선언했으니 그에 대한 정의가 Initialize 에서 이뤄져야 합니다. 프로젝트를 내리다보면 Initialize라는 메서드가 있습니다. 거기에 다음과 같이 작성해줍시다.



간단하게 설명하자면 우리는 골격 인식을 총 6군데에 대해서 받을 겁니다. 그리고 키넥트에서는 다양한 기능을 제공하는데 그중 골격 인식을 사용하기 위해서는 해당 기능을 활성화 시키고 시작하는 과정이 필요한 겁니다. 중간에 KinectSensor.KinectSensors[0] 이라고 된 것은 키넥트 중 첫번째 키넥트의 정보를 활용하겠다는 의미입니다. 즉 여러개의 키넥트가 달려있을 때는 해당 배열의 인덱스값을 바꿈으로서 하나의 프로젝트에서 계속 관리할 수 있다는 겁니다. 일단 우리는 하나의 키넥트만을 대상으로 하는 것이므로 첫번째 키넥트라는 뜻의 0을 삽입해준 겁니다. 

 그다음 start 바로 밑에 다음과 같이 작성해줍니다.



이게 C#에서 제공하는 이벤트 핸들러입니다. 어떤 프로그래밍 내부에서 해당 조건에 따른 state의 변화를 주기위해서는 위와 같은 이벤트가 필요하고 이를 다루는 구문을 이벤트 핸들러라고 합니다. 그리고 편리한 것은 위와 같이 +=키까지만 입력해주면 해당 부분의 이벤트코드를 자동으로 작성해줍니다. 여기서 탭버튼을 두번만 눌러주면



이렇게 골격을 인식할 수 있는 이벤트가 생성됩니다. 계속 안을 채워봅시다.


 키넥트 파트에서 계속 나온 이야기지만 모든 데이터는 Frame 단위로 넘어옵니다. 그게 Stream을 타고 컴퓨터로 오는 건데 그 정보를 skeletonFrame이라고 정한 자료형에 넣는 겁니다. 그래서 그게 있을 때에만 동작하게 하면 우리가 원하는 기능을 구현할 수 있겠지요. 그런데 그정보는 단순한 Frame이기 때문에 아까 앞에서 선언한 Skeleton형 자료형에 넣어주는 과정이 필요합니다. 그리고 골격이 몇개 인식되었는지를 확인하기 전에 초기 같은 지정해주면 좋겠지요. 앞에서 선언한 howmanySkels를 0으로 선언해줍니다. 



자 이제 골격이 인식될때마다 해당 수치를 증가시켜주면 되겠지요. C#에서는 조건문을 foreach 라는 문법을 둬서 그 조건이 성립할때마다 루프를 돌 수 있게끔 구성되어 있습니다. 



위 그림에서도 나오지만 키넥트가 골격을 인식할 수 있는 상태는 총 3가지로 나눠집니다. 추적을 못했을 때, 단순히 위치정보만 뽑을 때, 그리고 골격을 추적했을때 이렇게 3가지입니다. 당연히 저희 Tracked 일때를 지정해줘야됩니다. 마무리지으면 다음과 같습니다.


거의 끝났습니다. 그런데 우리가 결과를 확인하기 위해서는 위 내용을 출력하는 내용이 필요하겠지요. 그런데 XNA에서는 기존 WPF와는 다르게 Text라는 개념이 없습니다. 즉 WPF상에선 글자를 Text라는 속성으로 인식하지만 XNA에서는 하나의 그림으로 인식합니다. 그래서 이 '그림'을 출력하기 위해서는 하나의 폰트 이미지를 삽입해줘야 합니다. 



프로젝트의 Solution Explorer를 보면 밑에 이상한 Content라는 이름의 프로젝트가 하나더 있는 것이 보이실 겁니다. 거기서 새 아이템을 추가해줍니다. 4개의 요소가 보이는데 그중 우리가 선택할 것은 SpriteFont라는 겁니다. 그게 일종의 폰트 이미지 역할을 합니다.



참고로 기본 폰트형은 Segoe UI라는 윈도우 기본형 폰트입니다. 이걸 수정할 수 있긴 하지만 일단은 다음에 소개하도록 하겠습니다. 우선 이름을 font라고 하고 저장했습니다. 그럼 Solution Explorer 상에서도 그 폰트 이미지가 추가된 것을 확인할 수 있습니다. 자 이제 출력을 하기위해서는 어딘가에 코드를 작성해줘야 합니다.

일단 이 폰트도 컨텐츠이기 때문에 어딘가에서 불러와야 합니다. 그걸 앞에 나온 Initialize에서 정의해주면 됩니다.


 앞에서 잠깐 언급했었지만 무엇이든지 그리려면 이 프로젝트에선 Draw라는 메서드안에 집어넣어줘야 합니다. 폰트도 '그림' 이기 때문에 여기서 작성해주는 것이 맞지요. 프로젝트 맨 밑에 있습니다. 


XNA의 특성상 무언가를 뿌려주기 위해서는 Begin과 End의 과정을 거치고 그 안에서 어떤 동작이 정의되어야 합니다. 그래서 사람이 들어오면 howmanySkel의 값이 증가하면 그에 대한 값을 뿌려주게 되는 것이지요. 한번 결과를 확인해보겠습니다. 



중간에 목소리도 나오지만 중간에 사람이 개입하면 숫자가 증가하는 것을 확인할 수 있습니다. 이를 통해서 아까 Skeleton 자료형 속에 배열 하나당 사람 한명의 정보가 저장된 것을 알 수 있습니다. 즉 저 정보에서 더 끌어내면 골격 각각에 대한 정보도 얻을 수 있겠지요.

그럼 얼마나 많은 사람들이 저장될 수 있을까요? 프레임이 넘어오는 스트림을 보면 아시겠지만 총 8명까지 인식가능합니다. 플레이어의 인식은 총 8명까지 가능합니다. 그중에서도 골격까지 인식가능한 인원은 2명이 한계입니다. 만약 그 이상의 인원의 골격정보가 필요하다 싶으면 키넥트를 더 달면되겠지요. 


 근데 여기도 문제가 또 있습니다. 키넥트는 usb를 통해서 통신하는 기기입니다. usb의 특성상 정해진 대역폭 내에서 일정량이 전송되는데 키넥트는 그 대역폭이 조금 큰 편입니다. 그래서 만약 usb 허브를 사용한다던가 여러개의 usb 기기를 사용하는 사람이라면 키넥트의 동작이 무척 느릴겁니다. 저같은 경우도 조금 많은데 동작시 초기화되는 시간이 조금 긴 편입니다. 이러한 한계때문에 한없이 키넥트를 한 pc상에서 무한히 연결할 수도 없는 거지요. 이 때의 답은 어떤 일련의 서버를 거쳐서 네트워크가 형성된다면 이런 한계도 극복할 수 있을겁니다.

댓글