使用Kinect测量身高
使用Kinect测量身高的方法其实有很多种:
第一种方式是使用Kinect的视场角以及结合一些三角形几何运算,就可以大致测量出物体的高度,这一点在之前介绍深度影像处理的时候有提到。
第二种方式是使用Kinect骨骼追踪提供的20个关节点的相关坐标,在根据一定的算法测量出人体的身高。在Channel9上面的这个例子的一个分享,在这里拿过来和大家分享一下。在这里,根据臂展和身高有相似的关系,我对这个例子做了一点扩展,计算臂展来粗略计算身高,用臂展计算身高其实有个好处就是既可以使用正常模式(Normal model,20个关节点),可以使用坐姿模式(Seat model,10 个关节点),这样您坐着就可以测量身高,不过精度不保证哈。这里只是提供这么一个思路。
一. 计算身高的算法
Kinect获取的骨骼数据包含20个关节点的X,Y,Z坐标信息。您可能会想,为什么不直接使用头部(head)关节点和脚趾(foot)关节点之间的距离来直接计算身高。这样不准确,因为用户可能并没有站直,如果这样算的话,误差比较大。
另一个问题是,头部关节点给出的是头部中心点的位置,如果使用这个位置,您需要额外增加9至11厘米,但是即使这样,也不能达到百分之一百的准确,如果要更精确一点的话,可能需要使用深度影像值来提取头部的顶部位置。也不需要那么麻烦,现在来看看我们怎样使用骨骼关节点来计算高度信息。
仔细观察下面的骨骼点,可以看到,身高可以由下面几部分组成(如图中红色部分):
• 头部(Head) –肩膀中心(ShoulderCenter)
• 肩膀中心(ShoulderCenter) – 脊柱中心(Spine)
• 脊柱中心(Spine) – 髋部中心(HipCenter)
• 髋部中心(HipCenter) – 左或右膝关节(KneeLeft or KneeRight)
• 左膝关节KneeLeft(右膝关节KneeRight) – 左踝关节leLeft (右踝关节AnkleRight)
• 左踝关节leLeft (右踝关节AnkleRight)- 左脚FootLeft (右脚FootRight)
使用臂展计算身高,也可以计算图中绿色所示的关节点:
• 左手(HeadLeft) –左手腕(Wrist Left)
• 左手腕(Wrist Left) – 左胳膊肘(Elbow Left)
• 左胳膊肘(Elbow Left) – 左肩膀(Shoulder Left)
• 左肩膀(Shoulder Left)–肩膀中心(Shoulder Center)
• 肩膀中心(Shoulder Center)-右肩膀(Shoulder Right)
• 右肩膀(Shoulder Right)- 右胳膊肘 (Elbow Right)
• 右胳膊肘 (Elbow Right)- 右手腕(Wrist Right)
•右手腕(Wrist Right)- 右手 (Hand Right)
原理就是这样,下面来编代码实现。
二. 实现
程序界面很简单,展示20个关节点,然后显示计算结果。有几点需要说明:
首先,关节点的位置信息是三维的,下面公式用来计算两个关节点的距离
public static double Length(Joint p1, Joint p2){ return Math.Sqrt( Math.Pow(p1.Position.X - p2.Position.X, 2) + Math.Pow(p1.Position.Y - p2.Position.Y, 2) + Math.Pow(p1.Position.Z - p2.Position.Z, 2));}
上面的代码很直接。第二步,我们应该使用左腿还是右腿还进行测量更加准确呢,我们使用那个追踪的最好的。下面的代码用来计算腿部处于追踪状态的点的数量。如果那个数量多,那么就用那一个。
public static double Length(params Joint[] joints){ double length = 0; for (int index = 0; index < joints.Length - 1; index++) { length += Length(joints[index], joints[index + 1]); } return length;}
使用上面的函数,我们就可以判断是使用左腿还是右腿了。
// Find which leg is tracked more accurately.int legLeftTrackedJoints = NumberOfTrackedJoints(hipLeft, kneeLeft, ankleLeft, footLeft);int legRightTrackedJoints = NumberOfTrackedJoints(hipRight, kneeRight, ankleRight, footRight);double legLength = legLeftTrackedJoints > legRightTrackedJoints ? Length(hipLeft, kneeLeft, ankleLeft, footLeft) : Length(hipRight, kneeRight, ankleRight, footRight);
然后我们使用扩展方法,来计算骨骼的高度。下面是方法的代码:
public static double Height(this Skeleton skeleton){ const double HEAD_DIVERGENCE = 0.1; var head = skeleton.Joints[JointType.Head]; var neck = skeleton.Joints[JointType.ShoulderCenter]; var spine = skeleton.Joints[JointType.Spine]; var waist = skeleton.Joints[JointType.HipCenter]; var hipLeft = skeleton.Joints[JointType.HipLeft]; var hipRight = skeleton.Joints[JointType.HipRight]; var kneeLeft = skeleton.Joints[JointType.KneeLeft]; var kneeRight = skeleton.Joints[JointType.KneeRight]; var ankleLeft = skeleton.Joints[JointType.AnkleLeft]; var ankleRight = skeleton.Joints[JointType.AnkleRight]; var footLeft = skeleton.Joints[JointType.FootLeft]; var footRight = skeleton.Joints[JointType.FootRight]; // Find which leg is tracked more accurately. int legLeftTrackedJoints = NumberOfTrackedJoints(hipLeft, kneeLeft, ankleLeft, footLeft); int legRightTrackedJoints = NumberOfTrackedJoints(hipRight, kneeRight, ankleRight, footRight); double legLength = legLeftTrackedJoints > legRightTrackedJoints ? Length(hipLeft, kneeLeft, ankleLeft, footLeft) : Length(hipRight, kneeRight, ankleRight, footRight); return Length(head, neck, spine, waist) + legLength + HEAD_DIVERGENCE;}
同样滴,我们使用手,手腕,胳膊肘,肩膀,等9个关节点来计算臂展,并使用臂展来近似计算身高,下面的名为ArmExtendWith的扩展方法即为计算臂展的方法。
public static double ArmExtendWith(this Skeleton skeleton){ var armWidthDeviation = 0.5; var handRight = skeleton.Joints[JointType.HandRight]; var wristRight = skeleton.Joints[JointType.WristRight]; var elowRight = skeleton.Joints[JointType.ElbowRight]; var shoulderRight = skeleton.Joints[JointType.ShoulderRight]; var shoulderCenter = skeleton.Joints[JointType.ShoulderCenter]; var handleft = skeleton.Joints[JointType.HandLeft]; var wristleft = skeleton.Joints[JointType.WristLeft]; var elowleft = skeleton.Joints[JointType.ElbowLeft]; var shoulderleft = skeleton.Joints[JointType.ShoulderLeft]; // Calculate the left and right arm extends width double rightArmExtendsWidth = Length(handRight, wristRight, elowRight, shoulderRight, shoulderCenter); double leftArmExtendsWidth = Length(handleft, wristleft, elowleft, shoulderleft, shoulderCenter); return rightArmExtendsWidth + leftArmExtendsWidth + armWidthDeviation;}
最后,再SkeletonFrameReady事件中调用该方法即可。
void Sensor_SkeletonFrameReady(object sender, SkeletonFrameReadyEventArgs e){ using (var frame = e.OpenSkeletonFrame()) { if (frame != null) { canvas.Children.Clear(); Skeleton[] skeletons = new Skeleton[frame.SkeletonArrayLength]; frame.CopySkeletonDataTo(skeletons); var skeleton = skeletons.Where(s => s.TrackingState == SkeletonTrackingState.Tracked).FirstOrDefault(); if (skeleton != null) { // Calculate height. double height = Math.Round(skeleton.Height(), 2); double armExtendsWidth = Math.Round(skeleton.ArmExtendWith(), 2); // Draw skeleton joints. foreach (JointType joint in Enum.GetValues(typeof(JointType))) { DrawJoint(skeleton.Joints[joint].ScaleTo(640, 480)); } // Display height. tblHeight.Text = String.Format("Height: {0} m", height); tblArmExtendWidth.Text = String.Format("ArmWidth: {0} m", armExtendsWidth); } } }}
现在您可以运行代码查看结果了。
三. 结语
本文介绍了两种利用Kinect测量身高的方法,一种是之前讲过的,利用Kinect的视场角和物体构成的三角关系,运用几何运算,测量物体身高,第二种是利用Kinect提供的骨骼数据,根据关节点直接的距离,来计算人体的身高。本文着重讲解了利用骨骼关节点之间的距离计算身高的两种方法,一种是Channel9上面分享的利用头部、脊柱、髋关节、膝关节、踝关节等8个关节点的长度来计算身高,还有一种就是使用与臂展相关的,手、手腕、胳膊肘、肩膀等9个关节点信息,叫第一种方法相比,该方法可以使用坐姿模式进行计算。当然,可能精度不是很准确,本文只是提供了一些Kinect在测量身高方面的思路,源代码点击此处下载,希望本文对您有帮助!
TAG: