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 在阳光直射下(无论是在传感器上还是在用户身上)跟踪效果不佳 - 代码中是否需要考虑某些光照条件或其他外部因素?

我要找的答案是:

  1. 现场环境会出现什么样的问题?
  2. 您是如何编码或解决它的?

我像您以前一样创建了一个供家庭使用的应用程序,然后在 public 设置中展示了同一个应用程序。结果让我很尴尬,因为在受控环境中出现了很多我永远不会预料到的错误。但这确实对我有帮助,因为它让我对我的代码添加了一些有趣的调整,这些调整仅以人类检测为中心。

  1. 有条件检查一个"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;
    
  2. 使用玩家 ID 和索引来记住谁是谁

    这与上一个问题有关,如果 Kinect 将它正在跟踪的两个用户切换到其他用户,那么我的应用程序会因为数据的突然变化而崩溃。为了解决这个问题,我会跟踪每个玩家的骨骼索引和他们的玩家 ID。要详细了解我是如何做到这一点的,请参阅 Kinect user Detection

  3. 添加可调参数以适应不同情况

    在我展示的地方,相同的倾斜角度和其他基本 kinect 参数(如近距离模式)在新环境中不起作用。让用户能够调整其中一些参数,以便他们可以获得适合工作的最佳设置。

  4. 期待人们做傻事

    下次我介绍时,我有可调倾斜,你可以猜猜是否有人烧坏了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 属性。然而,最好的解决办法是在人们站立的地方铺一块简单的地毯。地毯也可以作为适当距离的指示,因此您可以提供更好的用户体验。