티스토리 뷰

Kinect

[kinect 프로젝트] SkeletalTracking Fundamentals

생각많은 소심남 2012. 2. 8. 12:17
지난 포스팅까지는 카메라와 개체와의 거리를 정보로 인지하는 Depth에 관한 내용을 다뤘었는데 이번에 하는 내용은 인체의 골격을 이용해서 이미지를 붙이고 정보를 얻는 Skeletal Tracking에 대해서 다뤄보도록 하겠습니다.
 

 


키넥트에선 인간의 골격을 총 20개의 부분으로 나눠서 인지합니다. 그걸 하나의 집합처럼 묶어서 처리하는 것이지요. 그래서 x,y,z 이렇게 3축으로 나눠진데 센서 인지범위에 따라 각 골격과 센서간의 거리가 결정됩니다.
 


 그리고 각 관절에 대한 정보를 세가지 상태로 나눠서 받을 수 있습니다. 그 관절을 따라가면서 또는 따라가지 않고, 아니면 적외선 방식으로 따라가는 방식이 있다고는 합니다. 그런데 사실 샘플을 돌려보신 분은 아시겠지만 적외선 방식은 사람의 위치를 정확히 인지하는 반면에 Track 방식으로는 가끔 사람의 위치에 따라서 잘못된 정보를 줄때가 있습니다. SDK 내에서는 이런 현상을 jitter라고 하며 이 걸 막기위해서 TransformSmoothParameters 를 쓴다고 합니다. 그래서 관절간의 연결이 좀더 부드럽게끔 도와줍니다. 동영상에서는 미술가가 Easing 하는 것에 빗대었습니다.
그래서 호를 그리며 그리는 방식과 직선을 그리며 그리는 방식을 구분하려면 위의 TransformSmmothParameters를 사용하는게 좋다고 합니다. 그래서 결론은 자신이 적용하고 싶은 어플리케이션에 따라서 쓰고 안쓰고가 결정된다는 이야기였습니다.


그리고 새로 나온 이야기지만 한 키넥트당 두명의 골격만 따라갈 수 있답니다. 물론 맨처음 포스팅에서 말했던 것처럼 키넥트 자체는 6개의 사물까지는 인지하지만 골격은 두개만 따라갈 수 있다는 겁니다. 그래서 각 골격당 고유의 ID가 부여됩니다. 이 말은 즉 그 ID에 대한 정보를 입력하면 실시간으로 관절에 대한 정보를 얻을 수 있지 않을까 하는 추측을 해봅니다. 밑의 skeletonStream은 실제로 코드 내에서 쓰는 방법에 대해서 언급하고 있습니다. 그래서 AppchooseSkeleton을 false로 설정한후 ChooseSkeleton 메서드를 사용해서 어떤 골격을 따라갈건지를 선택해주는 원리라고 설명하고 있습니다. 아마 이 부분은 데모에서 시연이 될 것 같습니다.


(프로젝트에 오류가 나네요. 압축 푸시고 5번 프로젝트를 여시면 됩니다.)

지난 번에 오류가 나온 것에 대해서 수정했습니다. 아마 디버깅 종료시 정상적으로 종료가 될겁니다.


 MainWindow를 보면 이미지가 총 3개가 포함되어있습니다. 이중 원 2개는 xaml상에서 Eillipse를 사용해서 만들어낸 개체이고 얼굴 사진은 외부에서 넣은 이미지를 xaml로 포함시킨 겁니다. 원본 이미지는 


이와 같이 c4f-color.png 파일로 프로젝트내에 포함되어 있습니다. 이 프로젝트를 디버깅해보면 원은 손 골격을 따라가고 얼굴 그림은 사람의 머리를 따라가게 될겁니다. 그리고 실제로 사람이 나오는 창은 MainWindow 내에 네모 안으로 한정될겁니다. 이 부분은 지지난 포스팅중에 KinectColorViewer라는 이름으로 했었습니다. 물론 이 부분은 xaml 상에서 클릭만으로 생성한겁니다. 

 
몇번이나 나온 내용이지만 KinectSensorChanged라는 이벤트가 실행되는 도중에 모든 기능들이 Enable이 되고 바로 이어지는 부분이 바로 AllFrameReady라는 메서드입니다. 여기서 이제 추적할 골격에 대한 정보를 설정해줍니다.
 


앞의 AllFrameReady에 나온 메서드중에 첫번째 골격을 잡는 GetFirstSkeleton() 메서드가 정의되어 있는 부분입니다. 앞에서 다뤘던 Color / Depth 와 마찬가지로 센서로부터 받은 이미지에서 골격에 대한 정보를 복사하는 작업이 들어갑니다. 그때도 보셨겠지만 보통 꼴이 Copy....DataTo()라는 형식으로 반복해서 나왔던 것 같네요.
중간에 나오는 allSkeletons에 대한 정보는 맨처음에


 라는 식으로 skeleton형 배열로 정의되어 있습니다. 그리고 바로 위에 정의되어 있는 것처럼 Skeleton을 총 6개로 나눠서 정보로 돌려주게 됩니다. 즉 다시 말하면 생성되는 Skeleton 배열은 6개가 되겠지요. 
 


다시 내려오면 이제 첫번째 골격은 어떻게 잡을 것인가에 대한 지정을 해주게 됩니다. 맨앞에서 언급한 것처럼 TrackingState에는 3개가 있습니다. 

 
아까는 Infered로 되어 있었지만 여기는 PositionOnly로 되어 있네요. 아무튼 예제에서는 Tracked 방식으로 골격을 따라가게 되어있습니다. 이로써 첫번째 골격에 대한 정보를 받을 준비를 마쳤습니다. 이제 다시 AllFrameReady 메서드로 돌아가서 내려오면 이제 할 건 센서의 초점을 어디에 맞출건가를 정하는 겁니다. 그래서 나오는 메서드가 GetCameraPoint입니다.


우선 배우는 중이니까 위에 Scaling 해주는 부분은 주석처리해주고 이 부분에 대한 정의를 살펴보겠습니다.
 

 
 여기서 주목해야 할건 바로 depth란 변수입니다. 이름은 depth라고 되어있지만 이 건 Depth로도 매핑이되고 Color로도 매핑이 됩니다. 그래서 코드를 더 보면 나오겠지만 모든 자료가 이 depth에서 나옵니다.
 


지금 하고 있는 건 관절의 타입을 정하고 그에 대한 위치, 즉 센서와 관절사이의 거리를 x,y,z로 나눠서 각각의 DepthPoint로 값을 넘겨줍니다. 앞에서도 말했듯이 JointType에는 20가지가 있습니다. 여기서 자신이 원하고자 하는 관절을 선택해주면 되겠지요.

 
이런식으로 지금은 머리와 양손에 대한 DepthPoint를 받아왔습니다. 위의 정보를 토대로 이제
각각의 x,y에 대한 정보를 이용해서 새로운 ColorImagePoint를 얻어와야 합니다.그 부분이 바로 밑에 진행되어 있습니다.


x,y로 정보를 받아오는게 보이시나요? 마지막으로는 받아올 Image의 포멧만 설정해주면 이부분도 끝나게 됩니다.
이 메서드의 마지막은 CameraPosition이란 메서드를 통해서 각 골격에 대한 위치를 잡아주는 건데 이부분에 대한 정의도 살펴보겠습니다.


 우리는 이전 메서드를 통해서 ColorPointImage를 받아왔습니다. 이제 어떤 개체를 이 PointImage에 씌울것인가를 결정하는게 바로 이 CameraPosition이 되겠습니다. 그럼 여기서 외 Width의 반을 잘라서 빼주는 걸까요?

일단 지금의 메서드를 잘 봐야합니다. 지금과 같이 안하고 Default 상태로 놔두게 된다면 따라가게 되는 이미지의 중심은 아마 아까 뽑아왔던 ColorImage의 (0 0) 지점에 잡혀있을겁니다. 보정을 하기 위해서는 그 개체의 폭과 길이를 잰다음 그 반을 빼주는 형식을 취해서 그 이미지의 중심이 정확히 골격으로 매핑이 되어야 할겁니다. 지금은 바로 그 과정을 거치고 있습니다. 물론 이 과정이 흔히 말하는 Calibration이랑은 다릅니다. 지금은 다만 골격으로 이미지의 중심을 맞춘거지요. 일단 여기까지 하고 디버깅을 합니다.


이미지는 잘 쫒아옵니다. 그런데 약간의 오차가 존재하기는 합니다. 그래서 이를 고쳐주는 과정이 바로 TransformSmoothParameter입니다. 물론 이 걸 통해서 원하는 대로 동작을 지정해줄 수 있습니다.


위의 KinectSensorChanged로 돌아가면 위와 같은 항목이 있습니다. 바로 이런식으로 자신이 원하는 방식으로 수치를 설정한후 변화를 줄수 있습니다. 밑의 Enable 메서드는 비활성화 시키고 다시 실행시켜봅니다.


 어떤가요? 이 과정을 거친거랑 위 동영상을 비교해보면 원의 이동이 조금 smooth해진 것을 느끼실겁니다. 몇번의 수치를 수정하다보면 사람에게 적정한 값을 구할 수 있지 않을까요?

자 다시 Enable(Parameters) 부분을 비활성화 시키고 그 밑 부분을 다시 활성화시킵니다. 이제 해볼건 Scaling부분입니다. 사실 사람이 카메라와 가까이 있으면 상관이 없는데 더 넓은 화면에 표현하기 위해서는 지금과 같이 하는 동작의 포인트를 늘리거나 줄일 수 있어야 합니다. 이를 위한 것이 바로 맨처음에 비활성화했던 ScalePosition이라는 겁니다.


다시 이 부분을 활성화시키고 밑의 정의로 봅니다.


바로 이 부분이 해당 포인트를 확장 또는 축소시켜주는 메서드입니다. 중심은 변하지 않습니다. 다만 그 중심이 스케일링되는 것이지요. 우선은 지금 활성화되어있는 scaledJoint 부분을 주석처리하고 위의 scaledJoint 부분을 활성화시킨 후 실행시켜보겠습니다.

아 그런데 조금 이상한게 있네요. 이부분은 실행해도 scale이 안됩니다. 디버깅을 해서 변수를 살펴봤는데


ScaledJoint가 HipCenter로 잡혀있습니다. 물론 이부분에 대한 정의는 코드내에는 없고요. 지금은 joint에서 값을 scale해서 덮어씌우는 꼴인데 왜 값이 다르게 나오는지 모르겠네요. 확인해보고 알려드리겠습니다.

여기까지가 코드로 진행하는 부분이었고 조금 재미있는 걸 삽입해보고자 합니다. xaml로 돌아가서
툴박스에 있는 SkeletalViewer를 선택한후 MainWindow상에 넣습니다. 물론 Width와 Height을 정하고 지난 시간에 한것처럼 Data Binding을 해줍니다. 그리고 실행시켜보면


여기까지 Skeletal tracking에 관한 QuickStart 설명이었습니다.

----------------------------------------------------------------------------------------------------------

ScaleTo가 안되는 이유를 찾았습니다. 스케일되는 장면을 확인하고 싶으신 분은 해당부분을 커맨트처리해주세요.


중간에 GetCameraPoint라는 메서드가 있습니다. 이부분에서 CameraPosition 부분을 커맨트 처리해줘야 스케일됩니다 밑이 동작화면입니다.



 정상적으로 돌아가는 것을 확인할 수 있습니다.

댓글