Kinect帧到达异步化

4

我希望您能协助我处理Kinect v2 SDK中的MultiSourceFrameArrived事件。

以下是相关方法:

    private async void _reader_MultiSourceFrameArrived(object sender, MultiSourceFrameArrivedEventArgs e)
    {
        MultiSourceFrame multiSourceFrame = e.FrameReference.AcquireFrame();

        using (var colorFrame = multiSourceFrame.ColorFrameReference.AcquireFrame())
        {
            if (colorFrame != null)
            {
                _writeableBitmap.Lock();
                colorFrame.CopyConvertedFrameDataToIntPtr(
                    _writeableBitmap.BackBuffer,
                    (uint)(_colorFrameDescription.Width * _colorFrameDescription.Height * _colorFrameDescription.BytesPerPixel),
                    ColorImageFormat.Bgra);
                _writeableBitmap.AddDirtyRect(new Int32Rect(0, 0, _writeableBitmap.PixelWidth, _writeableBitmap.PixelHeight));
                _writeableBitmap.Unlock();
                reflectionOverlayControl.ReflectionImageSource = _writeableBitmap;
            }
        }

        using (var bodyFrame = multiSourceFrame.BodyFrameReference.AcquireFrame())
        {
            if (bodyFrame != null)
            {
                Body body = JointHelpers.FindClosestBody(bodyFrame);

                if (body != null)
                {
                    if (body.IsTracked)
                    {
                        Dictionary<BodyComponentType, BodyComponent> bodyComponentDictionary = BuildBodyComponentDictionary(body);

                        foreach (BodyComponent bodyComponent in bodyComponentDictionary.Values.OrderBy(x => x.BodyComponentType))
                        {
                            bodyComponent.Generate(_writeableBitmap, _coordinateMapper, FrameType.Color, 25);
                            if (!_isRunningFiltering)
                            {
                                _isRunningFiltering = true;
                                try
                                { 
                                    await Task.Run(() =>
                                        {
                                            bodyComponent.RunFunFiltering();
                                        });
                                }
                                finally
                                {
                                    _isRunningFiltering = false;
                                }
                            }
                        }
                        reflectionOverlayControl.UpdateValues(
                               bodyComponentDictionary,
                               GetFullBodyComponent(body));
                    }
                }
            }
        }
    }

现在,请允许我解释:
  • 当从Kinect收到特定类型的帧时,该方法运行,这个帧被获取并且我可以在using块中提取出ColorFrame和BodyFrame。
  • 第一个“using”块将ColorFrame转换为WPF的WriteableBitmap(在构造函数中声明),并将用户控件的ReflectionImageSource设置为此WriteableBitmap。如果这是唯一的using块,我会看到屏幕上非常平滑的图像!
  • 第二个BodyFrame using确定最近的身体,如果被跟踪,则创建一个填充有人类BodyComponents(手、脚、头等)的字典。
  • 此处的foreach循环在每个BodyComponent上运行“Generate”函数,该函数设置其某些属性。例如,它设置了一个EncompassingRectangle属性,该属性是一个Int32Rect对象,旨在包含该组件。

下面就是我需要帮助的部分!

RunFunFiltering方法是一个高度密集的处理方法,运行时会创建一个阻塞语句,导致我的UI冻结。这将导致我的颜色帧视频反馈非常跳跃!RunFunFiltering方法需要设置一些BodyComponent类的属性,例如矩形应显示的颜色、ReflectionImageSource中的白色像素数以及将另一个可写位图设置为包含在矩形中的第一个ReflectionImageSource的部分。

由于此对象现在已经完成,所有属性都已设置(并且已对字典中的每个BodyComponent执行此操作),因此我在视图上运行UpdateValues方法,该方法为我显示BodyComponent类中有趣的内容。

根据@sstan在这篇文章中提供的建议:Async Await to Keep Event Firing

我添加了一个Task.Run()块。然而,这似乎没有释放我的UI,并且我仍然看到跳跃的图像。奇怪的是,在计时器示例中,它完全正常工作!我有点不知道该怎么办。

我对异步函数有点生疏,但我真的想了解您的解决方案。如果您能在代码中提供解释,我将非常感激!

更新

我已经确定获取帧的using语句在放置在Task.Run调用之外时会阻塞UI。

我不能只使整个BodyFrame using块异步运行,因为我需要第一个“Generate”函数始终发生并且不是重型线程的一部分。两个using块似乎不太优雅,而且问题被掩盖了...


PS - 我已经尝试让我的RunFunFiltering方法返回从Task.Run块中返回的内容。如果我在Run块之外将bodyComponent.Property设置为此值,我认为这可能会起作用。然而,我仍然遇到了同样的问题。 - Benjamin Biggs
你忘记写下当新的源帧到达时你想要什么了:(1)同时开始处理这个?(2)取消处理旧的并处理新的?还是(3)忽略新的,因为旧的还没有完成。方法(1)会导致很多线程同时处理。我知道这三个问题的答案,但是把它们都写下来太麻烦了,所以我首先需要这个规范。 - Harald Coppoolse
我想要在旧任务完成之前忽略新任务。这样做实际上会导致每n帧处理一次(其中n取决于任务完成的速度)。我希望这个更新能够有所帮助 - 我认为我已经进一步确定了问题所在。 - Benjamin Biggs
1个回答

0
从您的评论中,我了解到以下内容:
  • 当帧到达时,会调用异步函数
  • 如果没有运行 RunFunFiltering 任务,则启动一个
  • 如果已经有这样一个任务正在运行,则不要启动新的任务
  • 如果 RunFunFiltering 完成,则处理结果

.

Task taskFunFiltering = null;

private async Task ProcessFrame(...)
{   // a new frame is arrived
    DoSomeProcessing(...);
    // only start a new run fun filtering if previous one is finished
    if (taskFunFiltering == null || taskFunFiltering.IsCompleted)
    {   // start a new fun filtering
        // don't wait for the result
        taskFunFiltering = Task.Run( () => ...);
    }
}

private async Task RunFunFiltering(...)
{
    // do the filtering and wait until finished
    var filterResult = await DoFiltering(...);
    DisplayResult(filterResult);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接