Body Tracking
Azure Kinect Body Tracking은 Azure Kinect DK 장치와 Body Tracking SDK를 활용하여 사람의 신체 움직임을 실시간으로 추적하는 기술이다. 이 기술은 깊이 카메라와 IMU(관성 측정 장치)로부터 데이터를 수집하고, 이를 AI 기반 포즈 추정 알고리즘으로 처리하여 3D 신체 골격 정보를 제공한다. Azure Kinect SDK의 Body.cs 코드는 Azure Kinect에서 제공되는 데이터 처리, 변환, 저장, 복원을 포함하여 신체 추적 과정을 구현한다.
먼저, Azure Kinect 장치는 RGB 카메라와 깊이 카메라를 사용해 이미지를 캡처한다. 깊이 카메라는 ToF(Time of Flight)로 각 물체까지의 거리를 측정하며, 이 데이터를 Body Tracking SDK가 받아들여 사람의 신체 관절 정보를 추출한다. 관절 정보는 총 32개의 주요 관절로 구성되며, 각 관절의 3D 좌표, 방향(Quaternion), 그리고 신뢰도(Confidence Level)가 포함된다. 이러한 데이터를 활용해 관절의 위치와 움직임을 정밀하게 추적한다.
Body.cs 코드는 CopyFromBodyTrackingSdk 메서드를 통해 SDK에서 제공된 데이터를 Body 구조체에 복사한다. 관절의 3D 위치는 SDK의 데이터를 미터 단위로 변환하여 저장하며, 2D 좌표는 Calibration.TransformTo2D 메서드를 통해 깊이 데이터를 기반으로 계산한다. 관절의 방향은 사원수(Quaternion)로 표현되며, 신뢰도는 관절 추적의 품질을 나타낸다. 이를 통해 3D 공간에서 신체 움직임을 추적하고, 필요 시 2D 좌표를 생성해 화면에 투영할 수 있다. 추가적으로, 신체 데이터를 직렬화 및 역직렬화하는 기능도 포함한다. GetObjectData 메서드는 신체 데이터를 직렬화하여 저장하거나 네트워크로 전송할 수 있게 변환하며, 역직렬화 생성자는 저장된 데이터를 다시 Body 객체로 복원한다. 데이터 직렬화 과정에서는 3D 좌표, 2D 좌표, 방향 정보(Quaternion), 신뢰도 데이터를 각각 배열 형태로 분리하여 저장하며, 이 데이터는 이후 필요한 경우 복원하여 사용할 수 있다.
Azure Kinect Body Tracking의 핵심 원리는 깊이 카메라로부터 캡처한 데이터를 심층 학습 기반의 AI 모델로 처리하여 신체 골격을 추출하는 것이다. 이 모델은 대규모 데이터셋으로 학습되었으며, 다양한 자세와 환경에서 높은 정확도를 제공한다. SDK는 관절 위치와 방향 데이터를 프레임 단위로 업데이트하며, GPU 가속을 통해 실시간 처리 속도를 유지한다. 결론적으로 Body.cs는 Azure Kinect Body Tracking 데이터의 추출, 변환, 저장, 복원을 효율적으로 처리할 수 있도록 구성되어 있다. 신체 추적 데이터를 관리하기 위한 직렬화 및 복제 기능은 데이터 무결성과 확장성을 제공하며, 실시간 애플리케이션에서 안정적으로 동작하도록 설계되었다. 이러한 구조는 Azure Kinect의 장점인 높은 추적 정확도와 처리 속도를 최대한 활용하여 다양한 인터랙티브 시스템을 구축하는 데 기여하는 것이다.
Body.cs
using Microsoft.Azure.Kinect.BodyTracking;
using Microsoft.Azure.Kinect.Sensor;
using System;
using System.Numerics;
using UnityEngine;
using System.Runtime.Serialization;
// Class with relevant information about body
// bodyId and 2d and 3d points of all joints
[Serializable]
public struct Body : ISerializable
{
public System.Numerics.Vector3[] JointPositions3D;
public System.Numerics.Vector2[] JointPositions2D;
public System.Numerics.Quaternion[] JointRotations;
public JointConfidenceLevel[] JointPrecisions;
public int Length;
public uint Id;
public Body(int maxJointsLength)
{
JointPositions3D = new System.Numerics.Vector3[maxJointsLength];
JointPositions2D = new System.Numerics.Vector2[maxJointsLength];
JointRotations = new System.Numerics.Quaternion[maxJointsLength];
JointPrecisions = new JointConfidenceLevel[maxJointsLength];
Length = 0;
Id = 0;
}
public static Body DeepCopy(Body copyFromBody)
{
int maxJointsLength = copyFromBody.Length;
Body copiedBody = new Body(maxJointsLength);
for (int i = 0; i < maxJointsLength; i++)
{
copiedBody.JointPositions2D[i] = copyFromBody.JointPositions2D[i];
copiedBody.JointPositions3D[i] = copyFromBody.JointPositions3D[i];
copiedBody.JointRotations[i] = copyFromBody.JointRotations[i];
copiedBody.JointPrecisions[i] = copyFromBody.JointPrecisions[i];
}
copiedBody.Id = copyFromBody.Id;
copiedBody.Length = copyFromBody.Length;
return copiedBody;
}
public void CopyFromBodyTrackingSdk(Microsoft.Azure.Kinect.BodyTracking.Body body, Calibration sensorCalibration)
{
Id = body.Id;
Length = Microsoft.Azure.Kinect.BodyTracking.Skeleton.JointCount;
for (int bodyPoint = 0; bodyPoint < Length; bodyPoint++)
{
// K4ABT joint position unit is in millimeter. We need to convert to meters before we use the values.
JointPositions3D[bodyPoint] = body.Skeleton.GetJoint(bodyPoint).Position / 1000.0f;
JointRotations[bodyPoint] = body.Skeleton.GetJoint(bodyPoint).Quaternion;
JointPrecisions[bodyPoint] = body.Skeleton.GetJoint(bodyPoint).ConfidenceLevel;
var jointPosition = JointPositions3D[bodyPoint];
var position2d = sensorCalibration.TransformTo2D(
jointPosition,
CalibrationDeviceType.Depth,
CalibrationDeviceType.Depth);
if (position2d != null)
{
JointPositions2D[bodyPoint] = position2d.Value;
}
else
{
JointPositions2D[bodyPoint].X = Constants.Invalid2DCoordinate;
JointPositions2D[bodyPoint].Y = Constants.Invalid2DCoordinate;
}
}
}
public Body(SerializationInfo info, StreamingContext context)
{
float[] JointPositions3D_X = (float[])info.GetValue("JointPositions3D_X", typeof(float[]));
float[] JointPositions3D_Y = (float[])info.GetValue("JointPositions3D_Y", typeof(float[]));
float[] JointPositions3D_Z = (float[])info.GetValue("JointPositions3D_Z", typeof(float[]));
JointPositions3D = new System.Numerics.Vector3[JointPositions3D_X.Length];
for (int i = 0; i < JointPositions3D_X.Length; i++)
{
JointPositions3D[i].X = JointPositions3D_X[i];
JointPositions3D[i].Y = JointPositions3D_Y[i];
JointPositions3D[i].Z = JointPositions3D_Z[i];
}
float[] JointPositions2D_X = (float[])info.GetValue("JointPositions2D_X", typeof(float[]));
float[] JointPositions2D_Y = (float[])info.GetValue("JointPositions2D_Y", typeof(float[]));
JointPositions2D = new System.Numerics.Vector2[JointPositions2D_X.Length];
for (int i = 0; i < JointPositions2D_X.Length; i++)
{
JointPositions2D[i].X = JointPositions2D_X[i];
JointPositions2D[i].Y = JointPositions2D_Y[i];
}
float[] JointRotations_X = (float[])info.GetValue("JointRotations_X", typeof(float[]));
float[] JointRotations_Y = (float[])info.GetValue("JointRotations_Y", typeof(float[]));
float[] JointRotations_Z = (float[])info.GetValue("JointRotations_Z", typeof(float[]));
float[] JointRotations_W = (float[])info.GetValue("JointRotations_W", typeof(float[]));
JointRotations = new System.Numerics.Quaternion[JointRotations_X.Length];
for (int i = 0; i < JointRotations_X.Length; i++)
{
JointRotations[i].X = JointRotations_X[i];
JointRotations[i].Y = JointRotations_Y[i];
JointRotations[i].Z = JointRotations_Z[i];
JointRotations[i].W = JointRotations_W[i];
}
uint[] ConfidenceLevel = (uint[])info.GetValue("ConfidenceLevel", typeof(uint[]));
JointPrecisions = new JointConfidenceLevel[ConfidenceLevel.Length];
for (int i = 0; i < ConfidenceLevel.Length; i++)
{
JointPrecisions[i] = (JointConfidenceLevel)ConfidenceLevel[i];
}
Length = (int)info.GetValue("Length", typeof(int));
Id = (uint)info.GetValue("Id", typeof(uint));
}
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
float[] JointPositions3D_X = new float[Length];
float[] JointPositions3D_Y = new float[Length];
float[] JointPositions3D_Z = new float[Length];
for (int i = 0; i < Length; i++)
{
JointPositions3D_X[i] = JointPositions3D[i].X;
JointPositions3D_Y[i] = JointPositions3D[i].Y;
JointPositions3D_Z[i] = JointPositions3D[i].Z;
}
info.AddValue("JointPositions3D_X", JointPositions3D_X, typeof(float[]));
info.AddValue("JointPositions3D_Y", JointPositions3D_Y, typeof(float[]));
info.AddValue("JointPositions3D_Z", JointPositions3D_Z, typeof(float[]));
float[] JointPositions2D_X = new float[Length];
float[] JointPositions2D_Y = new float[Length];
for (int i = 0; i < Length; i++)
{
JointPositions2D_X[i] = JointPositions2D[i].X;
JointPositions2D_Y[i] = JointPositions2D[i].Y;
}
info.AddValue("JointPositions2D_X", JointPositions2D_X, typeof(float[]));
info.AddValue("JointPositions2D_Y", JointPositions2D_Y, typeof(float[]));
float[] JointRotations_X = new float[Length];
float[] JointRotations_Y = new float[Length];
float[] JointRotations_Z = new float[Length];
float[] JointRotations_W = new float[Length];
for (int i = 0; i < Length; i++)
{
JointRotations_X[i] = JointRotations[i].X;
JointRotations_Y[i] = JointRotations[i].Y;
JointRotations_Z[i] = JointRotations[i].Z;
JointRotations_W[i] = JointRotations[i].W;
}
info.AddValue("JointRotations_X", JointRotations_X, typeof(float[]));
info.AddValue("JointRotations_Y", JointRotations_Y, typeof(float[]));
info.AddValue("JointRotations_Z", JointRotations_Z, typeof(float[]));
info.AddValue("JointRotations_W", JointRotations_W, typeof(float[]));
uint[] ConfidenceLevels = new uint[Length];
for (int i = 0; i < Length; i++)
{
ConfidenceLevels[i] = (uint)JointPrecisions[i];
}
info.AddValue("ConfidenceLevels", ConfidenceLevels, typeof(uint[]));
info.AddValue("Length", Length, typeof(int));
info.AddValue("Id", Id, typeof(uint));
}
}
아래 코드는 이를 활용해 만든 키넥트로부터 추적된 눈의 위치를 실시간으로 반환하는 코드이다.
public Vector3 InputPositionEye(BackgroundData trackerFrameData)
{
int closestIdx = findClosestTrackedBody(trackerFrameData);
if (closestIdx == -1) return Vector3.zero;
var LeftEyePosition = trackerFrameData.Bodies[closestIdx].JointPositions3D[(int)JointId.EyeLeft];
var RightEyePosition = trackerFrameData.Bodies[closestIdx].JointPositions3D[(int)JointId.EyeRight];
Vector3 headPos = new Vector3(((float)LeftEyePosition.X + (float)RightEyePosition.X) / 2, ((float)LeftEyePosition.Y + (float)RightEyePosition.Y) / 2, ((float)LeftEyePosition.Z + (float)RightEyePosition.Z) / 2);
return headPos;
}
'Extended Reality > XR & Spatial Computing SDK' 카테고리의 다른 글
XR & Spatial Computing SDK [1] : 스냅 드래곤 스페이스 (SnapDragon Spaces by Qualcom) (0) | 2024.11.19 |
---|