Microsoft Kinect 和 background/environmental 噪音
Microsoft Kinect and background/environmental noise
我目前正在 Windows 8.1 上使用 Microsoft Kinect for Windows SDK 2 进行编程。一切进展顺利,与 'real world'.
相比,在家庭开发环境中显然没有太多背景噪音
我想向那些有'real world' Kinect 应用程序经验的人寻求一些建议。 Kinect(尤其是 v2)在有路人、旁观者和背景中意想不到的物体的实时环境中表现如何?我确实希望,在从 Kinect 传感器到用户的 space 中通常不会有干扰,但是我现在非常注意的是背景噪音本身。
虽然我知道 Kinect 在阳光直射下(无论是在传感器上还是在用户身上)跟踪效果不佳 - 代码中是否需要考虑某些光照条件或其他外部因素?
我要找的答案是:
- 现场环境会出现什么样的问题?
- 您是如何编码或解决它的?
我像您以前一样创建了一个供家庭使用的应用程序,然后在 public 设置中展示了同一个应用程序。结果让我很尴尬,因为在受控环境中出现了很多我永远不会预料到的错误。但这确实对我有帮助,因为它让我对我的代码添加了一些有趣的调整,这些调整仅以人类检测为中心。
有条件检查一个"human"的有效性。
当我在有许多其他物体和道具的演示楼层中间展示我的应用程序时,我发现即使是椅子也可能在短时间内被误认为是人,这导致我的应用程序在用户和无生命之间切换对象,导致它无法跟踪用户并丢失他们的进度。为了应对这种或其他误报的人类检测,我添加了自己的额外人类检查。我最成功的方法是比较人体的比例。我以主机为单位实施了此测量。 (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 第一个版本对我来说的问题,我不知道有多少问题在第二个版本中已经解决了,因为遗憾的是我还没有动手一个。但是,如果不是备用技术,我仍然会实施其中的一些技术,因为会有例外,尤其是在计算机视觉中。
Outlaw Lemur 详细描述了您在 real-world 场景中可能遇到的大部分问题。
使用Kinect for Windows version 2,不需要调整电机,因为没有电机,传感器视野更大。这会让你的生活更轻松。
我想补充以下提示和建议:
1) 避免直射光(物理或内部照明)
Kinect 有一个可能会混淆的红外传感器。该传感器不应与任何光源直接接触。您可以在您的 home/office 上通过使用普通的激光笔和手电筒来模拟这样的环境。
2) 如果您只跟踪一个人,select最近的被跟踪用户
如果您的应用只需要一个播放器,则该播放器需要 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) 有跟踪哪个玩家的视觉线索
如果播放器周围有很多人,您可能需要显示 on-screen 跟踪了谁。您可以突出显示深度帧位图或使用 Microsoft 的 Kinect Interactions。
This is an example of removing the background and keeping the player pixels only.
7) 避免光面地板
有些地板(明亮、有光泽)可能会映出人,而 Kinect 可能会混淆他们的某些关节(例如,Kinect 可能会将您的腿伸向反射 body)。如果您无法避免光面地板,请使用 BodyFrame 的 FloorClipPlane 属性。然而,最好的解决办法是在人们站立的地方铺一块简单的地毯。地毯也可以作为适当距离的指示,因此您可以提供更好的用户体验。
我目前正在 Windows 8.1 上使用 Microsoft Kinect for Windows SDK 2 进行编程。一切进展顺利,与 'real world'.
相比,在家庭开发环境中显然没有太多背景噪音我想向那些有'real world' Kinect 应用程序经验的人寻求一些建议。 Kinect(尤其是 v2)在有路人、旁观者和背景中意想不到的物体的实时环境中表现如何?我确实希望,在从 Kinect 传感器到用户的 space 中通常不会有干扰,但是我现在非常注意的是背景噪音本身。
虽然我知道 Kinect 在阳光直射下(无论是在传感器上还是在用户身上)跟踪效果不佳 - 代码中是否需要考虑某些光照条件或其他外部因素?
我要找的答案是:
- 现场环境会出现什么样的问题?
- 您是如何编码或解决它的?
我像您以前一样创建了一个供家庭使用的应用程序,然后在 public 设置中展示了同一个应用程序。结果让我很尴尬,因为在受控环境中出现了很多我永远不会预料到的错误。但这确实对我有帮助,因为它让我对我的代码添加了一些有趣的调整,这些调整仅以人类检测为中心。
有条件检查一个"human"的有效性。
当我在有许多其他物体和道具的演示楼层中间展示我的应用程序时,我发现即使是椅子也可能在短时间内被误认为是人,这导致我的应用程序在用户和无生命之间切换对象,导致它无法跟踪用户并丢失他们的进度。为了应对这种或其他误报的人类检测,我添加了自己的额外人类检查。我最成功的方法是比较人体的比例。我以主机为单位实施了此测量。 (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 第一个版本对我来说的问题,我不知道有多少问题在第二个版本中已经解决了,因为遗憾的是我还没有动手一个。但是,如果不是备用技术,我仍然会实施其中的一些技术,因为会有例外,尤其是在计算机视觉中。
Outlaw Lemur 详细描述了您在 real-world 场景中可能遇到的大部分问题。
使用Kinect for Windows version 2,不需要调整电机,因为没有电机,传感器视野更大。这会让你的生活更轻松。
我想补充以下提示和建议:
1) 避免直射光(物理或内部照明)
Kinect 有一个可能会混淆的红外传感器。该传感器不应与任何光源直接接触。您可以在您的 home/office 上通过使用普通的激光笔和手电筒来模拟这样的环境。
2) 如果您只跟踪一个人,select最近的被跟踪用户
如果您的应用只需要一个播放器,则该播放器需要 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) 有跟踪哪个玩家的视觉线索
如果播放器周围有很多人,您可能需要显示 on-screen 跟踪了谁。您可以突出显示深度帧位图或使用 Microsoft 的 Kinect Interactions。
This is an example of removing the background and keeping the player pixels only.
7) 避免光面地板
有些地板(明亮、有光泽)可能会映出人,而 Kinect 可能会混淆他们的某些关节(例如,Kinect 可能会将您的腿伸向反射 body)。如果您无法避免光面地板,请使用 BodyFrame 的 FloorClipPlane 属性。然而,最好的解决办法是在人们站立的地方铺一块简单的地毯。地毯也可以作为适当距离的指示,因此您可以提供更好的用户体验。