以高帧速率显示图像

发布于 2024-09-29 03:36:22 字数 1210 浏览 5 评论 0原文

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

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

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

这是我尝试过的:

  • 一个简单的计时器(或 DispatcherTimer) - 对于较慢的帧速率非常有效,但我无法让它超过 60 FPS。

  • 单线程无限循环 - 速度相当快,但我必须将 DoEvents/它的 WPF 等效项放入循环中,以便重绘窗口; 例如某些控件的按键事件未被触发等。

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

     new Thread(() =>;
            {
                while(仍在捕获)
                {
                    if (Camera.CheckForAndDownloadImage(CameraInstance))
                    {
                        this.Dispatcher.Invoke((Action)this.DisplayImage);
                    }
                }
            })。开始();
    

    嗯,这相对来说效果不错,但是会给 CPU 带来相当大的负载,当然,如果机器没有多个 CPU/核心,就会完全杀死机器,这是不可接受的。另外,我用这种方式存在大量线程争用。

问题很明显 - 是否有更好的替代方案,或者在这种情况下其中一种是可行的方法?

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

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

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

感谢所有的答案。

here's the problem: I have a custom hardware device and I have to grab images from it in C#/WPF and display them in a window, all with 120+ FPS.

The problem is that there is no event to indicate the images are ready, but I have to constantly poll the device and check whether there are any new images and then download them.

There are apparently a handful of ways to do it, but I haven't been able to find the right one yet.

Here's what I tried:

  • A simple timer (or DispatcherTimer) - works great for slower frame rates but I can't get it past let's say, 60 FPS.

  • An single threaded infinite loop - quite fast but I have to put the DoEvents/it's WPF equivalent in the loop in order for window to be redrawn; this has some other unwanted (strange) consequences such as key press events from some controls not being fired etc..

  • Doing polling/downloading in another thread and displaying in UI thread, something like this:

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

    Well, this works relatively well, but puts quite a load on a CPU and of course completely kills the machine if it doesn't have more than one CPU/core, which is unacceptable. Also, I there is a large number of thread contentions this way.

The question is obvious - are there any better alternatives, or is one of these the way to go in this case?

Update:
I somehow forgot to mention that (well, forgot to think about it while writing this question), but of course I don't need all frames to be displayed, however I still need to capture all of them so they can be saved to a hard drive.

Update2:
I found out that the DispatcherTimer method is slow not because it can't process everything fast enough, but because the DispatcherTimer waits for the next vertical sync before firing the tick event; which is actually good in my case, because in the tick event I can save all pending images to a memory buffer (used for saving images to disk) and display just the last one.

As for the old computers being completely "killed" by capturing, it appears that WPF falls back to software rendering which is very slow. There's probably nothing I can do about.

Thanks for all the answers.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

月竹挽风 2024-10-06 03:36:22

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

a) 将 Thread.Sleep(5) 放入轮询循环中,这应该允许您接近 120fps,同时仍保持较低的 CPU 时间。

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

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

就我个人而言,我会按这个顺序实施它们。 a 或 b 很可能会解决您的问题。

I think you're trying for too simplistic of an approach. Here's what I would do.

a) put a Thread.Sleep(5) in your polling loop, that should allow you to get close to 120fps while still keeping CPU times low.

b) Only update the display with every 5th frame or so. That will cut down on the amount of processing as I'm not sure that WPF is made to handle much more than 60fps.

c) Use ThreadPool to spawn a subtask for each frame that will then go and save it to the disk (in a seperate file per frame), that way you won't be as limited by disk performance. Extra frames will just pile up in memory.

Personally I would implement them in that order. Chances are a or b will fix your problems.

極樂鬼 2024-10-06 03:36:22

您可以执行以下操作(所有伪代码):
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(无法记住参数)
    

    {
    图像图像显示;
    锁(_locker){imageToDisplay = _captures[k.Count - 1];
    显示函数(图像到显示);
    让调度程序

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

You could do the following (all psuedocode):
1. Have a worker thread running dealing with the capture process:

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


            }
        }
    }).Start();
  1. Have the dispatcher timer thread take latest captured image (obviously it will have missed some captures since last tick) and display. Therefore, UI thread is throttled and doing as little as possible, it isn't doing all the "capturing", this is done by worker threads. sorry I can't get this bit to format (but you get the idea):

    void OnTimerTick(can't remember params)
    

    {
    Image imageToDisplay;
    lock(_locker){imageToDisplay = _captures[k.Count - 1];
    DisplayFunction(imageToDisplay);
    }

  2. it might be that the list is a queue and another thread is used to bleed the queue and write to disk or whatever.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文