我目前正在使用Windows 8.1上的Microsoft Kinect for Windows SDK 2进行编程。事情进展顺利,在家庭开发的环境中,与“现实世界”相比,背景中没有太多噪音。
我想向那些有真实世界经验的人寻求建议。使用Kinect的应用程序。 Kinect(尤其是v2)如何在现场环境中与过路人,旁观者和背景中的意外物体相比?我确实希望,在从Kinect传感器到用户的空间中,通常不会有干扰 - 我现在非常注意的是背景噪声。
虽然我知道Kinect在阳光直射下(在传感器或用户身上)不能很好地跟踪 - 我是否需要将某些光照条件或其他外部因素纳入代码?
我正在寻找的答案是:
答案 0 :(得分:5)
Outlaw Lemur详细描述了您在实际场景中可能遇到的大多数问题。
使用Kinect for Windows版本2,您无需调整电机,因为没有电机且传感器具有更大的视野。这将使您的生活更轻松。
我想补充以下提示和建议:
1)避免直射光(物理或内部照明)
Kinect有一个可能会混淆的红外传感器。该传感器不应与任何光源直接接触。您可以通过使用普通的激光指示器和手电筒来模拟家庭/办公室中的这种环境。
2)如果您只跟踪一个人,请选择最近的被跟踪用户
如果您的应用只需要一个播放器,则该播放器需要a)完全跟踪并且b)比其他播放器更靠近传感器。这是一种简单的方法,可以让参与者了解谁被跟踪,而不会使您的UI更复杂。
public static Body Default(this IEnumerable<Body> bodies)
{
Body result = null;
double closestBodyDistance = double.MaxValue;
foreach (var body in bodies)
{
if (body.IsTracked)
{
var position = body.Joints[JointType.SpineBase].Position;
var distance = position.Length();
if (result == null || distance < closestBodyDistance)
{
result = body;
closestBodyDistance = distance;
}
}
}
return result;
}
3)使用跟踪ID区分不同的玩家
每位玩家都有一个TrackingID属性。当玩家干涉或随机移动时使用该属性。但是,不要使用该属性作为面部识别的替代方法。
ulong _trackinfID1 = 0;
ulong _trackingID2 = 0;
void BodyReader_FrameArrived(object sender, BodyFrameArrivedEventArgs e)
{
using (var frame = e.FrameReference.AcquireFrame())
{
if (frame != null)
{
frame.GetAndRefreshBodyData(_bodies);
var bodies = _bodies.Where(b => b.IsTracked).ToList();
if (bodies != null && bodies.Count >= 2 && _trackinfID1 == 0 && _trackingID2 == 0)
{
_trackinfID1 = bodies[0].TrackingId;
_trackingID2 = bodies[1].TrackingId;
// Alternatively, specidy body1 and body2 according to their distance from the sensor.
}
Body first = bodies.Where(b => b.TrackingId == _trackinfID1).FirstOrDefault();
Body second = bodies.Where(b => b.TrackingId == _trackingID2).FirstOrDefault();
if (first != null)
{
// Do something...
}
if (second != null)
{
// Do something...
}
}
}
}
4)当玩家离传感器太远或太近时显示警告。
为了获得更高的准确度,玩家需要站在特定的距离:不要太远或太靠近传感器。以下是检查方法:
const double MIN_DISTANCE = 1.0; // in meters
const double MAX_DISTANCE = 4.0; // in meters
double distance = body.Joints[JointType.SpineBase].Position.Z; // in meters, too
if (distance > MAX_DISTANCE)
{
// Prompt the player to move closer.
}
else if (distance < MIN_DISTANCE)
{
// Prompt the player to move farther.
}
else
{
// Player is in the right distance.
}
5)始终知道玩家何时进入或离开场景。
Vitruvius提供了一种简单的方法来了解有人进入或离开现场的时间。
Here is the source code以下是如何在您的应用中使用它:
UsersController userReporter = new UsersController();
userReporter.BodyEntered += UserReporter_BodyEntered;
userReporter.BodyLeft += UserReporter_BodyLeft;
userReporter.Start();
void UserReporter_BodyEntered(object sender, UsersControllerEventArgs e)
{
// A new user has entered the scene. Get the ID from e param.
}
void UserReporter_BodyLeft(object sender, UsersControllerEventArgs e)
{
// A user has left the scene. Get the ID from e param.
}
6)有一个关于跟踪哪个玩家的直观线索
如果玩家周围有很多人,您可能需要在屏幕上显示被跟踪的人。您可以突出显示深度框架位图或使用Microsoft的Kinect交互。
This is an example of removing the background and keeping the player pixels only
7)避免使用光滑的地板
有些楼层(明亮,有光泽)可能会反映人物,而Kinect可能会混淆一些关节(例如,Kinect可能会将您的腿伸展到反射的身体)。如果您无法避免使用光滑的地板,请使用BodyFrame的FloorClipPlane属性。然而,最好的解决方案是拥有一个简单的地毯,您希望人们站在那里。地毯也可以作为适当距离的指示,因此您可以提供更好的用户体验。
答案 1 :(得分:4)
我像以前一样创建了一个家庭使用的应用程序,然后在公共场合展示了相同的应用程序。结果让我感到尴尬,因为在受控制的环境中我会发现许多错误。然而,这确实对我有所帮助,因为它让我为我的代码添加了一些有趣的调整,这些调整仅以人类检测为中心。
有条件检查“人”的有效性。
当我在展示楼层中间展示我的应用程序时,我发现即使是椅子也可能会在短时间内被人误认为是导致我的应用程序在用户和无生命对象之间切换,导致它失去用户跟踪并失去进度。为了对抗这种或其他假阳性的人类检测,我为人类添加了我自己的额外检查。我最成功的方法是比较人体的比例。我用头部单位测量了这个。 (head units picture)下面是我如何做到这一点的代码(SDK版本1.8,C#)
bool PersonDetected = false;
double[] humanRatios = { 1.0f, 4.0, 2.33, 3.0 };
/*Array indexes
* 0 - Head (shoulder to head)
* 1 - Leg length (foot to knee to hip)
* 2 - Width (shoulder to shoulder center to shoulder)
* 3 - Torso (hips to shoulder)
*/
....
double[] currentRatios = new double[4];
double headSize = Distance(skeletons[0].Joints[JointType.ShoulderCenter], skeletons[0].Joints[JointType.Head]);
currentRatios[0] = 1.0f;
currentRatios[1] = (Distance(skeletons[0].Joints[JointType.FootLeft], skeletons[0].Joints[JointType.KneeLeft]) + Distance(skeletons[0].Joints[JointType.KneeLeft], skeletons[0].Joints[JointType.HipLeft])) / headSize;
currentRatios[2] = (Distance(skeletons[0].Joints[JointType.ShoulderLeft], skeletons[0].Joints[JointType.ShoulderCenter]) + Distance(skeletons[0].Joints[JointType.ShoulderCenter], skeletons[0].Joints[JointType.ShoulderRight])) / headSize;
currentRatios[3] = Distance(skeletons[0].Joints[JointType.HipCenter], skeletons[0].Joints[JointType.ShoulderCenter]) / headSize;
int correctProportions = 0;
for (int i = 1; i < currentRatios.Length; i++)
{
diff = currentRatios[i] - humanRatios[i];
if (abs(diff) <= MaximumDiff)//I used .2 for my MaximumDiff
correctProportions++;
}
if (correctProportions >= 2)
PersonDetected = true;
我成功的另一种方法是找到关节总和的平均值。我发现非人类检测具有更多可变的总距离,而人类则更加一致。我使用单维支持向量机学习的平均值(我发现用户的总距离通常小于9)
//in AllFramesReady or SkeletalFrameReady
Skeleton data;
...
float lastPosX = 0; // trying to detect false-positives
float lastPosY = 0;
float lastPosZ = 0;
float diff = 0;
foreach (Joint joint in data.Joints)
{
//add the distance squared
diff += (joint.Position.X - lastPosX) * (joint.Position.X - lastPosX);
diff += (joint.Position.Y - lastPosY) * (joint.Position.Y - lastPosY);
diff += (joint.Position.Z - lastPosZ) * (joint.Position.Z - lastPosZ);
lastPosX = joint.Position.X;
lastPosY = joint.Position.Y;
lastPosZ = joint.Position.Z;
}
if (diff < 9)//this is what my svm learned
PersonDetected = true;
使用玩家ID和索引来记住谁是谁
这与之前的问题相关,如果Kinect将其正在跟踪的两个用户切换到其他用户,那么由于数据的突然变化,我的应用程序将崩溃。为了解决这个问题,我会跟踪每个玩家的骨骼指数和他们的玩家ID。要了解有关我如何执行此操作的详细信息,请参阅Kinect user Detection。
添加可调节参数以适应不同情况
在我演示的地方,相同的倾斜角度和其他基本的kinect参数(如近模式)在新环境中不起作用。让用户能够调整其中一些参数,以便他们可以获得最佳的工作设置。
期待人们做蠢事
下次我演示的时候,我有可调节的倾斜度,你可以猜出是否有人烧坏了Kinect的电机。任何可以打破Kinect的东西,都会有人破产。在文档中留下警告是不够的。你应该在Kinect的硬件上添加警告检查,以确保人们在无意中破坏某些东西时不会感到沮丧。以下是一些代码,用于检查用户是否在两分钟内使用电机超过20次。
int motorAdjustments = 0;
DateTime firstAdjustment;
...
//in motor adjustment code
if (motorAdjustments == 0)
firstAdjustment = DateTime.Now;
++motorAdjustments;
if (motorAdjustments < 20)
{
//adjust the tilt
}
else
{
DateTime timeCheck = firstAdjustment;
if (DateTime.Now > timeCheck.AddMinutes(2))
{
//reset all variables
motorAdjustments = 1;
firstAdjustment = DateTime.Now;
//adjust the tilt
}
}
我会注意到所有这些对我来说都是第一版Kinect的问题,我不知道在第二版中有多少已经解决了,因为我遗憾的是还没有完成任何一个版本。但是,如果没有备份技术,我仍会实施其中一些技术,因为会有例外情况,特别是在计算机视觉方面。