Kinect传感器不仅在人体动作捕捉方面表现出色,它在识别3D空间中的地面平面方面同样令人惊叹。原生的Kinect SDK为提供了准确识别特定平面是否为地面所需的所有信息。还可以获取诸如Kinect传感器放置高度等信息,甚至可以测量3D空间中某点与地面的距离。所有这些功能对现代增强现实应用有着巨大的影响。
在今天的教程中,将向展示以下内容:
这是将要开发的一个快照:
要运行本文提供的代码和示例,需要以下条件:
源代码托管在GitHub上。此外,所有这些功能都是Vitruvius的一部分。Vitruvius是最流行的Kinect框架,将帮助轻松创建Kinect应用程序。所以,如果急于开发,只需下载Vitruvius并将其集成到应用程序中。
Kinect SDK已经内置了一些地面检测功能。请见FloorClipPlane属性。FloorClipPlane是BodyFrame类的一个成员。很少有人知道它的存在,然而,它是开发运动应用程序时最有用的组件之一。
以下是如何访问FloorClipPlane属性的方法:
using (BodyFrame frame = e.FrameReference.AcquireFrame()) {
if (frame != null) {
Vector4 floorClipPlane = frame.FloorClipPlane;
}
}
如所见,FloorClipPlane是一组4个浮点值(Vector4):X、Y、Z和W。X、Y和Z值指示平面在3D空间中的方向。W值指示平面与坐标系原点之间的距离。实际上,Vector4结构表示平面的数学方程。可以在Wolfram MathWorld上阅读更多相关信息。
对于示例,将FloorClipPlane向量封装到一个方便的Floor C#类中:
public class Floor {
public float X { get; internal set; }
public float Y { get; internal set; }
public float Z { get; internal set; }
public float W { get; internal set; }
public Floor(Vector4 floorClipPlane) {
X = floorClipPlane.X;
Y = floorClipPlane.Y;
Z = floorClipPlane.Z;
W = floorClipPlane.W;
}
}
正如所看到的,W值指示地面与坐标系原点之间的距离。Kinect坐标系的原点是设备本身!因此,FloorClipPlane的W参数描述了Kinect传感器放置的高度!使用W值,可以向用户提供反馈:
Vector4 floorClipPlane = frame.FloorClipPlane;
float height = floorClipPlane.W;
if (height < 1f) // 1米
{
Debug.WriteLine("The sensor is positioned too low!");
}
当W值为0时,意味着视野受限。这是了解是否有任何物体,如桌子、椅子等阻挡视野的非常简单的方法。
if (height == 0f) {
Debug.WriteLine("The field of view is limited.");
}
除了找到传感器的高度,还可以检测设备的倾斜角度。所需要做的就是使用地面平面的方向:
public double Tilt {
get {
return Math.Atan(Z / Y) * (180.0 / Math.PI);
}
}
检测到的地面是一个已知距离(W)和方向(X、Y、Z)的平面。3D空间中的点是一组X、Y和Z坐标。
// Plane (X, Y, Z, W)
Floor floor = new Floor(frame.FloorClipPlane);
// Point (X, Y, Z)
CameraSpacePoint point = body.Joints[JointType.WristLeft].Position;
要测量一点与平面之间的距离,需要使用点-平面距离公式:
将如何将这个公式转换为编程代码?让为做数学运算:
public double DistanceFrom(CameraSpacePoint point) {
double numerator = X * point.X + Y * point.Y + Z * point.Z + W;
double denominator = Math.Sqrt(X * X + Y * Y + Z * Z);
return numerator / denominator;
}
将此方法添加到Floor.cs类中。就是这样!现在可以得到任何点的3D位置,将其作为参数传递给上述方法,并找到它与地面的距离。
double distance = floor.DistanceFrom(point);
供参考,这是完整的Floor.cs类:
public class Floor {
public float X { get; internal set; }
public float Y { get; internal set; }
public float Z { get; internal set; }
public float W { get; internal set; }
public Floor(Vector4 floorClipPlane) {
X = floorClipPlane.X;
Y = floorClipPlane.Y;
Z = floorClipPlane.Z;
W = floorClipPlane.W;
}
public float Height {
get {
return W;
}
}
public double Tilt {
get {
return Math.Atan(Z / Y) * (180.0 / Math.PI);
}
}
public double DistanceFrom(CameraSpacePoint point) {
double numerator = X * point.X + Y * point.Y + Z * point.Z + W;
double denominator = Math.Sqrt(X * X + Y * Y + Z * Z);
return numerator / denominator;
}
}