【问题标题】:Displaying images with a high frame rate以高帧率显示图像
【发布时间】:2010-10-28 11:58:21
【问题描述】:

问题是:我有一个自定义硬件设备,我必须在 C#/WPF 中从中获取图像并将它们显示在一个窗口中,所有这些都具有 120+ FPS。

问题是没有事件表明图像准备好了,但我必须不断地轮询设备并检查是否有任何新图像然后下载它们。

显然有几种方法可以做到这一点,但我还没有找到合适的方法。

这是我尝试过的:

  • 一个简单的计时器(或 DispatcherTimer) - 适用于较慢的帧速率,但我无法超过 60 FPS。

  • 单线程无限循环 - 相当快,但我必须将 DoEvents/它的 WPF 等效项放入循环中,以便重绘窗口;这会产生一些其他不需要的(奇怪)后果,例如某些控件未触发的按键事件等。

  • 在另一个线程中进行轮询/下载并在 UI 线程中显示,如下所示:

     new Thread(() =>
            {
                while (StillCapturing)
                {
                    if (Camera.CheckForAndDownloadImage(CameraInstance))
                    {
                        this.Dispatcher.Invoke((Action)this.DisplayImage);
                    }
                }
            }).Start();
    

    嗯,这工作得比较好,但是会给 CPU 带来相当大的负载,如果机器没有超过一个 CPU/内核,当然会完全杀死机器,这是不可接受的。另外,我这种方式有大量的线程争用。

问题很明显 - 有没有更好的选择,或者在这种情况下可以采用其中一种方法吗?

更新:
我不知何故忘了提到这一点(好吧,写这个问题时忘了考虑它),但我当然不需要显示 all 帧,但我仍然需要捕获所有帧所以它们可以保存到硬盘上。

更新 2: 我发现 DispatcherTimer 方法不是因为它不能足够快地处理所有事情,而是因为 DispatcherTimer 在触发滴答事件之前等待下一次垂直同步;在我的情况下这实际上很好,因为在滴答事件中我可以将所有待处理的图像保存到内存缓冲区(用于将图像保存到磁盘)并只显示最后一个。

至于被捕获完全“杀死”的旧计算机,WPF 似乎退回到非常慢的软件渲染。我可能无能为力。

感谢大家的回答。

【问题讨论】:

  • 120+ FPS?您使用哪种显示技术作为我的 TFT 显示器(高清)不会真正超过 60Hz,因此无论如何它都会丢失一半的帧。
  • 谢谢。你是绝对正确的。不知道我是怎么没有想到的(嗯,我想到了,但它从我的脑海中溜走了)。我仍然需要将它们全部捕获到内存中(以便将它们保存到磁盘)并显示其中的一半 - 我会用计时器尝试一下,看看是否可以通过。
  • 我认为每 4 帧显示一次就足够了 (30FPS),并且可能有助于 CPU 负载。你捕捉什么分辨率?硬件设备的接口是否“可拆分”一个连接到记录计算机第二个连接到用户界面计算机?

标签: c# wpf multithreading camera frame-rate


【解决方案1】:

我认为您正在尝试一种过于简单化的方法。这就是我要做的。

a) 在您的轮询循环中放置一个 Thread.Sleep(5),这应该可以让您接近 120fps,同时仍然保持较低的 CPU 时间。

b) 仅每 5 帧左右更新一次显示。这将减少处理量,因为我不确定 WPF 是否能够处理超过 60fps 的速度。

c) 使用 ThreadPool 为每个帧生成一个子任务,然后将其保存到磁盘(在每帧单独的文件中),这样您就不会受到磁盘性能的限制。额外的帧只会堆积在内存中。

就我个人而言,我会按此顺序实施它们。机会是 a 或 b 将解决您的问题。

【讨论】:

  • 谢谢!我已经使用了您描述的方法 c) 并结合 DispatcherTimer 实现了方法 b)(请参阅我对原始帖子的最后更新),目前我对结果非常满意。
【解决方案2】:

您可以执行以下操作(所有伪代码): 1. 有一个工作线程运行处理捕获进程:

List<Image> _captures = new List<Image>();
 new Thread(() =>
    {
        while (StillCapturing)
        {
            if (Camera.CheckForAndDownloadImage(CameraInstance))
            {
                lock(_locker){_captures.Add(DisplayImage);


            }
        }
    }).Start();
  1. 让调度程序计时器线程获取最新捕获的图像(显然,自上次滴答以来它会错过一些捕获)并显示。因此,UI 线程被限制并尽可能少地做,它没有做所有的“捕获”,这是由工作线程完成的。抱歉,我无法格式化这个位(但你明白了):

    void OnTimerTick(can't remember params)
    

    { 图像图像显示; 锁定(_locker){imageToDisplay = _captures[k.Count - 1]; 显示功能(图像显示); }

  2. 可能是该列表是一个队列,而另一个线程用于排空队列并写入磁盘或其他任何内容。

【讨论】:

  • 如果其他线程持有锁的时间过长,锁可能会导致帧被跳过。另外,它仍然没有解决OP提到的高CPU负载问题。
  • 锁定不会导致图像被跳过。一个线程将捕获所有图像。调度程序线程将在调度时渲染最新的图像(显然是跳过图像)。
猜你喜欢
  • 1970-01-01
  • 2012-10-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-11
  • 1970-01-01
相关资源
最近更新 更多