WinForms:如何显示 Usb3 Vision 网络摄像头的输出

Posted

技术标签:

【中文标题】WinForms:如何显示 Usb3 Vision 网络摄像头的输出【英文标题】:WinForms: How to show output of Usb3 Vision webcam 【发布时间】:2021-12-21 11:12:20 【问题描述】:

我的任务是尽快显示来自相机的帧。相机是Basler Dart,每秒可以产生超过70帧。不幸的是它不支持流输出,但它会生成位图。

现在我们的解决方案类似于 Basler 示例中的解决方案,但它使用PictureBox 来显示帧。我在某处读到它很慢,应该使用更好的解决方案。我同意它很慢,因为在显示所有 70 fps 时它会占用 25% 的 CPU(仅相机,应用程序的其余部分仅占用 5%)。不幸的是,我还没有找到更好的解决方案。

private void OnImageGrabbed(Object sender, ImageGrabbedEventArgs e)

  if (InvokeRequired)
  
    // If called from a different thread, we must use the Invoke method to marshal the call to the proper GUI thread.
    // The grab result will be disposed after the event call. Clone the event arguments for marshaling to the GUI thread.
    BeginInvoke( new EventHandler<ImageGrabbedEventArgs>( OnImageGrabbed ), sender, e.Clone() );
    return;
  

  try
  
    // Acquire the image from the camera. Only show the latest image. The camera may acquire images faster than the images can be displayed.

    // Get the grab result.
    IGrabResult grabResult = e.GrabResult;

    // Check if the image can be displayed.
    if (grabResult.IsValid)
    
      // Reduce the number of displayed images to a reasonable amount if the camera is acquiring images very fast.
      if (!stopWatch.IsRunning || stopWatch.ElapsedMilliseconds > 100)
      
        stopWatch.Restart();
           
        bool reqBitmapOldDispose=true;

        if(grConvBitmap==null || grConvBitmap.Width!=grabResult.Width || grConvBitmap.Height!=grabResult.Height)
        
          grConvBitmap = new Bitmap(grabResult.Width, grabResult.Height, PixelFormat.Format32bppRgb);
          grConvRect=new Rectangle(0, 0, grConvBitmap.Width, grConvBitmap.Height);
        
        else
          reqBitmapOldDispose=false;
           
        // Lock the bits of the bitmap.
        BitmapData bmpData = grConvBitmap.LockBits(grConvRect, ImageLockMode.ReadWrite, grConvBitmap.PixelFormat);
        // Place the pointer to the buffer of the bitmap.
        converter.OutputPixelFormat = PixelType.BGRA8packed;
        IntPtr ptrBmp = bmpData.Scan0;
        converter.Convert( ptrBmp, bmpData.Stride * grConvBitmap.Height, grabResult );
        grConvBitmap.UnlockBits( bmpData );
          
        // Assign a temporary variable to dispose the bitmap after assigning the new bitmap to the display control.
        Bitmap bitmapOld = pictureBox.Image as Bitmap;
        // Provide the display control with the new bitmap. This action automatically updates the display.
        pictureBox.Image = grConvBitmap;
        if (bitmapOld != null && reqBitmapOldDispose)
        
          // Dispose the bitmap.
          bitmapOld.Dispose();
        
      
    
  
  catch (Exception exception)
  
    ShowException(exception, "OnImageGrabbed" );
  
  finally
  
    e.DisposeGrabResultIfClone();
  

我的想法是将负载转移到 GPU。我尝试过 SharpGL (OpenGL for C#),但它似乎不能每秒消耗 70 个纹理,但我想这是因为我在 60 分钟内完成了 OpenGL 学习基础的解决方案。

我的问题是:我应该使用什么来代替 PictureBox 来提高功率并减少 CPU 负载?我应该使用 OpenGL 还是只限制显示的帧(如示例所示)? PC 只有集成显卡(Intel i3 第 6 代)。

【问题讨论】:

【参考方案1】:

总体而言,在性能方面,我建议您进行一些分析以查看实际瓶颈在哪里。如果您有一些数据要继续,而不是仅仅猜测问题,那么发现性能问题要容易得多。一个好的分析器还应该告诉你一些关于垃圾收集惩罚和分配率的信息。

总的来说,我会说示例代码看起来相当不错:

有一些速率控制,即使 100 毫秒/10 fps 的限制在我看来相当低。 据我所知,似乎没有太多不必要的复制。 看起来您正在重用和更新位图,而不是每帧都重新创建它。

您可以尝试一些可能的事情:

如果相机是单色相机,您可能会跳过转换阶段,只需将内存从抓取缓冲区复制到位图即可。 如果相机是高分辨率型号,您可以考虑合并像素以降低图像的分辨率。 我们在 wpf 中使用 WritableBitmap,性能相当好。但我不确定它与 winforms 图片框相比如何。 您可以尝试自己绘制,即将一个事件处理程序附加到面板的OnPaint 并使用Graphics.DrawImage* 方法之一绘制最新的位图。我不知道这是否会产生显着的性能差异。 如果转换需要很长时间,您可以尝试在后台线程上执行此操作。如果您这样做,您可能需要某种方式来处理位图,以便不能同时从工作线程和 UI 线程访问位图。 可能会改进速率控制以“以 UI 线程可以处理的速度处理图像”而不是固定速率。

要考虑的另一件事是使用了硬件渲染。这通常是这种情况,但在某些情况下,Windows 会回退到软件渲染,从而导致 CPU 使用率非常高:

服务器可能根本没有 GPU 虚拟化可能无法虚拟化 GPU 远程桌面可能使用软件渲染

【讨论】:

以上是关于WinForms:如何显示 Usb3 Vision 网络摄像头的输出的主要内容,如果未能解决你的问题,请参考以下文章

如何将 Vision 输出显示到 UI 中?

Winforms - 如何在设计器中显示/隐藏元素?

如何让 WinForms 应用程序全屏显示

如何在 C# Winforms 程序的文本框中突出显示文本?

如何在 Winforms 中显示“正在加载...请稍候”消息以获取长加载表单?

BackgroundWorker,我怎样才能显示真正的进步? C# Winforms