在健身领域,跟踪举重杆的位置对于监测锻炼进度至关重要。今天,将向展示如何使用Kinect传感器来识别举重杆,并计算它与地面的距离。这样,就可以日复一日地跟踪进步,并检查性能如何。这是一个将在完成本教程后实现的视频示例——请记住,是一名工程师,而不是健身教练!
很酷,对吧?让看看如何在代码中实现它…
要开始这个项目,需要以下条件:
像往常一样,为提供了完整的源代码以及演示视频。
算法的核心是BarDetectionEngine
类。这个类封装了下面将要介绍的所有细节。不会展示完整的源代码,因为它超过500行C#代码。将解释代码背后的思考过程和逻辑。
传统的对象跟踪是通过处理RGB彩色图像来完成的。Kinect提供了一种令人难以置信的方式来理解环境。这种手段被称为“深度处理器”。使用其激光传感器,Kinect可以为提供512×424点的点云,以及每个点与传感器平面之间的水平距离。
深度数组中的每个点只是一个原始距离值(以毫米为单位)。要跟踪杆,需要检测与手有相似距离的点。以下是如何访问深度数组的方法:
using (
var depthFrame = frame.DepthFrameReference.AcquireFrame())
{
if (depthFrame != null)
{
depthFrame.CopyFrameDataToArray(_depthData);
}
}
Kinect SDK为提供了一个方便的帧源:BodyIndex。BodyIndexFrames指定了上述深度点中哪些属于人体!结合Depth和BodyIndex帧,可以提取每个属于人的距离。
using (
var bodyIndexFrame = frame.BodyIndexFrameReference.AcquireFrame())
{
if (bodyIndexFrame != null)
{
bodyIndexFrame.CopyFrameDataToArray(_bodyIndexData);
}
}
检测玩家手的位置也很简单。如果想了解如何使用Kinect进行关节跟踪,请参考前一个教程。
var handLeft = body.Joints[JointType.HandLeft].Position;
var handRight = body.Joints[JointType.HandRight].Position;
现在,已经知道了手在3D空间中的位置(X, Y, Z)。也知道视野中每个点的Z距离!由于举重杆是由手抓住的,它的点会靠近手点。因此,将遍历深度数组,找到那些具有相似Z距离的点。
ushort depth = depthData[depthIndex];
尽管上述代码应该有效,但有一个主要问题:当杆非常靠近身体时,算法会挑选属于胸部、臀部或肩膀的深度点!显然,这些点不属于杆。这正是需要身体索引帧的原因!身体索引帧确定特定点是否属于身体。因此,可以排除属于身体的点,保留其余的点。这就是实现这一功能的代码:
if (bodyIndexData[depthIndex] != 0xff)
{
// 这个点不属于玩家。
}
可选地,可以平滑值并指定最小/最大距离或杆旋转。在代码中,设置了一个25度的杆旋转阈值。
查看完整的BarDetectionEngine.cs
文件。
由于有了属于杆的深度点,现在可以通过取最左边和最右边的点来计算它的长度。也可以找到这些点在2D颜色空间中的坐标。
就是这样,伙计们。可以探索源代码,看看是如何可视化杆并测量各种其他属性的。本教程的目的是展示使用KinectSDK v2处理原始深度帧的强大功能。
上述所有功能都封装在单个类中——BarDetectionEngine。可以按照以下方式使用BarDetectionEngine:
// 1) 初始化
var barDetectionEngine = new BarDetectionEngine(sensor.CoordinateMapper, colorWidth, colorHeight, depthWidth, depthHeight);
barDetectionEngine.BarDetected += BarDetectionEngine_BarDetected;
// 2) 更新
barDetectionEngine.Update(depthData, bodyIndexData, body);
// 3) 事件处理
private void BarDetectionEngine_BarDetected(object sender, BarDetectionResult e)
{
if (e != null)
{
var center = e.Trail;
var height = e.BarHeight;
var length = e.barLength;
var left = e.Minimum;
var right = e.maximum;
}
}