티스토리 뷰

사실 지난 포스팅을 끝으로 SDK 관련 내용은 안 다루려고 했는데.. 하는 일이 계속 SDK를 공부하게끔 만듭니다.

( 참 이번에 Imagine Cup 2012 키넥트분야로 2라운드에 진출했습니다.)

뭐 아무튼 계속 공부해야 될 듯하네요. 지난 시간까지 Voice Recorder를 만들어서 그걸 테스트해보는 프로젝트까지 진행했습니다. 이번 시간에 할 내용은 진짜 본격적으로 사람을 추적할 수 있는 Skeletal Tracking을 XNA와 곁들여서 진행해보고자 합니다.

물론 이전에 언급한 Color나 Depth에 대한 정보도 중요하긴 하지만 NUI(Natural User Interface)를 구성하는 가장 근본적인 요소는 바로 Skeleton 입니다. 일단 사람을 인지해야 사람의 손을 추적하거나 모션을 인식할 수 있기 때문입니다. 단순히 Color나 Depth만 가지고는 그런걸 구현하기 힘듭니다.

거의 처음 포스팅때 설명했던 내용입니다. 오른쪽에 있는 이미지가 바로 다빈치의 인체비례도입니다. 여기에 따르면 인간의 골격은 중간중간에 있는 관절이 뼈로 연결되어 있는 형태라고 언급하고 있습니다. SDK에서는 그런 내용에서 따와서 Skeleton을 20개의 관절(joint)와 19개의 뼈(bone)로 인식하게끔 했습니다. 그 각각의 관절 정보를 3차원적으로 인식해서 좌표로 찍는 것이지요. 왼쪽에 나와있는 3축상으로 나타내어 Z축상에 센서가 놓여져 있다고 가정했을때,

X축은 센서와의 거리가 각도적인 측면과 함께 고려해서 측정되고,

Y축은 거리가 높이적 측면과 고려되서 측정됩니다. 당연히 Z축은 일직선상에서 센서와의 거리겠지요.

각 관절 정보는 총 3가지 상태로 나눠져서 측정되는데 이에 대한 언급은 프로그래밍을 하면서 다시 언급하겠습니다.


우선 XNA 프로젝트로 SkeletonTrack 이라고 이름짓고 생성합니다. 그 후에 관련 레퍼런스를 첨가해주고 using 지시자로 포함시켜줍니다.

물론 초기 변수 지정시 KinectSensor를 하나 추가시켜줘야하겠지요?

자 이제 무엇을 활성시키느냐 SkeletalTracking을 위해서는 LoadContent에서 다음 작업을 반드시 해줘야합니다. 

많이 봐 오신 것처럼 활성화시키고 시작하는 과정은 똑같습니다. 그리고 아래와 같이 +=까지 작성하신 후 탭키를 딱 두번만 눌러주면 이벤트도 자동으로 생성됩니다. 이 이벤트에 채워야 할 내용은 이벤트 형성시 각각의 데이터를 받아오는 과정이 필요한데 여기도 Color나 Depth와 마찬가지로 배열이 필요합니다. Color는 Byte, Depth는 Short으로 배열을 구성했지만 Skeletal은 조금 받아들이기 쉽습니다. 그렇게 받아오는 변수 자료형이 Skelton이기 때문입니다. 그와 더불어 이벤트는 다음과 같이 구성해줍니다.

모든 과정이 거의 똑같습니다. 간단하게 언급하자면 이벤트가 활성화됬을때 Skeletons의 정보가 frame 안에 복사가 되는 형식이 되겠습니다. 이 부분에서 중요한 것은 이 이벤트는 Draw나 Update를 할때는 이 메서드가 동작하지 말아야 합니다.일반적으로 XNA에서는 다른 포스팅에서도 설명했었지만 Update를 하고 난후에 Draw로 그려지는 건데 이렇게 되면 약간의 딜레이가 발생합니다. 그 순간은 아주 짧겠지만 Draw로 그려질때쯤, 키넥트는 또다른 Skeleton Data를 받아서 전달되겠지요. 이렇게 된다면 결론적으로 이 정보는 XNA의 동작과 동기화가 되면 안되는 겁니다. 동기화가 된다면 지난 포스팅에서 소개해드린 것처럼 동작순간에는 다른 동작을 할 수 없기 때문에 계속적인 병목현상이 발생하겠지요. 이 부분에 대한 설명은 나중에 하고 일단은 Draw 메서드를 작성하겠습니다.

foreach 구문이 나오는데요. for의 조건문 꼴이라고 할까요? Skeletons 라고 하여 아까 받은 정보 중에서 s의 정보만 빼서 그 정보를 토대로 Skeleton을 그리는 형식입니다. 눈치는 채셨겠지만 Skeleton은 다른 color나 Depth와 같이 메서드만 쳐주면 끝이 아니라, 뼈대를 그려주는 작업이 필요합니다. 여기서는 이를 DrawSkeleton이라고 메서드를 만든 것이고요. 물론 이에 대한 정의도 따로 해줘야 하겠지요.

참 앞에서 관절의 상태를 3가지로 나눠서 판단한다고 간단히 언급했는데 이를 확인할 수 있는 부분이 SkeletonTrackingState입니다. 이 변수에 마우스를 대고 우클릭을 하시면 Go To Definition 항목이 있습니다. 이를 통해서 따라 들어가면 다음과 같은 정의가 있습니다.

여기가 바로 SkeletonTrackingState가 정의되어 있는 곳입니다. 각각 추적이 안될때는 NotTracked, 관절의 위치만 뽑을때는 PositionOnly(이전 베타버전때는 Infrared로 표현되었습니다.), 그걸 추적할때는 Tracked 상태로 설정이 되겠고 어딘가는 case 구문에 따라 상태가 나눠지겠지요. 이번 프로젝트에서는 사람의 골격을 계속 따라다녀야 하므로 Tracked 상태를 판단하여 그때 전체 Skeleton을 그리고자 합니다. 이제는 이 DrawSkeleton을 봐야 합니다. 

 우리의 골격을 형성하는 요소는 아까 앞에서도 잠깐 언급했습니다만, 관절과 뼈가 있겠지요. 이게 합쳐져야 전체적인 Skeleton이 형성될 겁니다. 그럼 우선적으로 DrawSkeleton 메서드를 작성하기에 앞서서 관절 사이를 연결해주는 Drawline와 그 라인대로 뼈를 그려주는 DrawBone 메서드를 작성해야 할겁니다. 우선 Drawline부터 작성하겠습니다. 일단은 content에 Add New Item을 통해서 4x4 White ball을 하나 생성합니다.

이걸 메인 코드에서 써먹어야 겠지요. Texture2D로 부릅니다. 그 후에 Drawline를 작성합니다.

역시 수학을 잘해야 한다는게 여기서 드러납니다. 이 부분은 뭐라고 설명을 못하겠네요. 아무튼 line값을 구하기 위해서 각각의 scale과 angle을 구하고 그 위치에 아까 지정한 Texture2D로 하나의 line이 형성되고 여기에 색과 함께 입혀지는 겁니다. 다음은 DrawBone입니다. 여기에는 KinectSensor의 속성 함수인 MapSkeletonPointToColor 메서드가 등장합니다. 

해당 위치에 대한 joint 포인트를 잡고 그 점을 다시 새로운 vector로 표시한겁니다. 물론 이같은 것을 j2에 관해서도 취해주고 마지막으로 위에서 언급한 Drawline에 이 점들을 써먹어야 합니다.

이렇게 마무리 지어주면 되겠지요. 이제 대망의 DrawSkeleton입니다. 그런데 이 부분은 의외로 간단합니다. 우리가 무슨점을 써야 할지를 알고 있으니까 그 포인트를 잡으면 됩니다. 아까 인체비례도에 나와있는 그 20개의 포인트를 각 점으로 사용하면 됩니다. 예시는 다음과 같습니다.



앞에서 만들어둔 DrawBone에 각 JointType을 매핑하면 됩니다. 이걸 복사해서 Type만 바꾸면 되겠지요. 그럼 앞에서 말한대로 19개의 뼈가 나옵니다. 

이게 바로 인체 비례도에 따른 뼈의 배치입니다. 

마지막으로 아까 삽입한 Joint 이미지를 LoadContent에서 Load하면 끝입니다.

자 이제 결과물을 확인하면 되겠습니다.

제가 사실 처음에 잘 못 판단한건 Texture2D의 변수명을 Joint로 하는거였습니다. 사실 이게 Joint가 아니라 뼈 하나하나를 그리는 점이었는데 말입니다. 다른분들은 다른 변수명으로 지정해주시기 바랍니다.(왜냐하면 Joint라는 자료형이 또 있습니다...)


아무튼 XNA에서도 SkeletalTracking... 잘 됩니다!


댓글