微软Kinect和背景/环境噪音

Fra*_*Kim 6 kinect kinect.toolbox kinect-sdk kinect-interaction

我目前正在使用Windows 8.1上的Microsoft Kinect for Windows SDK 2进行编程.事情进展顺利,在家庭开发环境中,与"现实世界"相比,背景中没有太多噪音.

我想向那些具有Kinect"真实世界"应用经验的人寻求建议.Kinect(尤其是v2)如何在现场环境中与过路人,旁观者和背景中的意外物体相比?我确实希望,在从Kinect传感器到用户的空间中,通常不会有干扰 - 我现在非常注意的是背景噪声.

虽然我知道Kinect在阳光直射下(无论是在传感器上还是在用户身上)都不能很好地跟踪 - 我是否需要将某些照明条件或其他外部因素纳入代码?

我要找的答案是:

  1. 在现场环境中会出现什么样的问题?
  2. 你是如何编写代码或以自己的方式工作的?

Van*_*gos 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;
    }
Run Code Online (Sandbox Code Playgroud)

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...
                }
            }
        }
    }
Run Code Online (Sandbox Code Playgroud)

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.
}
Run Code Online (Sandbox Code Playgroud)

5)始终知道玩家何时进入或离开场景.

Vitruvius提供了一种简单的方法来了解有人进入或离开现场的时间.

以下是源代码,以下是如何在您的应用中使用它:

    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.
    }
Run Code Online (Sandbox Code Playgroud)

6)有一个关于跟踪哪个玩家的直观线索

如果玩家周围有很多人,您可能需要在屏幕上显示被跟踪的人.您可以突出显示深度框架位图或使用Microsoft的Kinect交互.

这是删除背景并仅保留播放器像素的示例.

7)避免光滑的地板

一些楼层(明亮,有光泽)可能会反映人,而Kinect可能会混淆一些关节(例如,Kinect可能会将您的腿伸展到反射的身体).如果无法避免光滑的地板,请使用BodyFrame的FloorClipPlane属性.然而,最好的解决方案是拥有一个简单的地毯,您希望人们站在那里.地毯也可以作为适当距离的指示,因此您可以提供更好的用户体验.

  • 事实上,如果地板太光滑,Kinect可能会将你的腿伸展到反射的身体. (3认同)

Lia*_*roy 4

我像您之前一样创建了一个供家庭使用的应用程序,然后在公共环境中展示了相同的应用程序。结果让我很尴尬,因为有很多错误是我在受控环境下无法预料到的。然而,这确实对我有帮助,因为它让我对我的代码添加了一些有趣的调整,这些调整仅以人类检测为中心。

  1. 具备检验“人”的有效性的条件。

    当我在带有许多其他物体和道具的演示厅中间展示我的应用程序时,我发现即使是椅子也可能在短暂的时间内被误认为是人,这导致我的应用程序在用户和无生命的物体之间切换,导致它失去对用户的追踪并失去他们的进度。为了应对这种或其他误报的人类检测,我添加了自己的额外人类检查。我最成功的方法是比较人体的比例。我以头部单位来实现这一测量。(头单元图片)下面是我如何做到这一点的代码(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;
    
    Run Code Online (Sandbox Code Playgroud)

    我成功使用的另一种方法是求关节之间距离平方和的平均值。我发现非人类检测具有更多可变的总距离,而人类则更加一致。我使用单维支持向量机学到的平均值(我发现用户的总距离通常小于 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;
    
    Run Code Online (Sandbox Code Playgroud)
  2. 使用玩家 ID 和索引来记住谁是谁

    这与上一问题相关,如果 Kinect 将其正在跟踪的两个用户切换到其他用户,那么我的应用程序将由于数据的突然变化而崩溃。为了解决这个问题,我会跟踪每个玩家的骨骼索引和他们的玩家 ID。要了解有关如何执行此操作的更多信息,请参阅Kinect 用户检测

  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
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)

    我要指出的是,所有这些对我来说都是 Kinect 第一个版本的问题,而且我不知道其中有多少问题在第二个版本中得到了解决,因为遗憾的是我还没有得到一个版本。然而,如果不是备用技术,我仍然会实现其中一些技术,因为会有例外,特别是在计算机视觉领域。