【问题标题】:Show byte array image in WPF with high refresh rate在 WPF 中以高刷新率显示字节数组图像
【发布时间】:2021-10-11 04:24:43
【问题描述】:

上下文

我有一台 Basler 相机,它会在捕获新图像时引发事件。
在事件 arg 中,我可以将图像抓取为字节数组。

我必须对此图像进行计算,然后将其显示在 WPF 应用程序中。相机刷新率高达40FPS。

问题并找到解决方案

可以在此处找到将字节数组转换为 WPF 图像的解决方案:Convert byte array to image in wpf
这个解决方案非常适合只转换一次字节数组,但是我觉得在 40FPS 时会丢失很多内存。每次都会创建一个新的 BitmapImage() 并且不能被释放。

是否有更好的解决方案来在 WPF 中显示一个变化高达 40 FPS 的字节数组?(可以完全重新考虑处理问题的方式)

代码

这种在 WPF 中显示相机流的解决方案有效,但 BitmapImage image = new BitmapImage(); 行对我来说并不好。

private void OnImageGrabbed(object sender, ImageGrabbedEventArgs e)
{
    // Get the result
    IGrabResult grabResult = e.GrabResult;
    if (!grabResult.GrabSucceeded)
    {
        throw new Exception($"Grab error: {grabResult.ErrorCode} {grabResult.ErrorDescription}");
    }

    // Make process on the image
    imageProcessor.Process(grabResult);

    // Convert grabResult in BGR 8bit format
    using Bitmap bitmap = new Bitmap(grabResult.Width, grabResult.Height, PixelFormat.Format32bppRgb);
    BitmapData bmpData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadWrite, bitmap.PixelFormat);
    IntPtr ptrBmp = bmpData.Scan0;
    converter.Convert(ptrBmp, bmpData.Stride * bitmap.Height, grabResult);
    bitmap.UnlockBits(bmpData);

    // Creat the BitmapImage
    BitmapImage image = new BitmapImage(); // <-- never Disposed !
    using (MemoryStream memory = new MemoryStream())
    {
        bitmap.Save(memory, ImageFormat.Bmp);
        memory.Position = 0;
        image.BeginInit();
        image.StreamSource = memory;
        image.CacheOption = BitmapCacheOption.OnLoad;
        image.EndInit();
        image.Freeze();
    }
    LastFrame = image; // View is binded to LastFrame
}

【问题讨论】:

标签: c# wpf image


【解决方案1】:

在每一帧上,你都是

  • 创建位图
  • 将其编码为 MemoryStream
  • 创建位图图像
  • 将 MemoryStream 解码为 BitmapImage

最好创建一次WritableBitmap,然后反复调用它的WritePixels方法。

您可能仍需要转换原始缓冲区,因为 WPF 似乎没有 PixelFormat.Format32bppRgb 的等效项 - 或者它可能是 PixelFormats.Bgr32

var wb = LastFrame as WriteableBitmap;

if (wb == null)
{
    wb = new WriteableBitmap(
        grabResult.Width, grabResult.Height,
        96, 96, PixelFormats.Bgr32, null);
    LastFrame = wb;
}

wb.WritePixels(...);

【讨论】:

    【解决方案2】:

    我建议您使用 WriteableBitmap 来显示结果。这避免了重新分配 UI 图像的需要。如果源中的像素格式与位图中的像素格式匹配,您可以简单地使用WritePixels 来更新图像。

    请注意,您只能从主线程修改 WriteableBitmap,并且 ImageGrabbed 事件将在后台线程上引发。一旦事件处理程序返回,grabResult 将被处理掉。所以你需要ask the UI thread 来进行实际的更新,并且你需要一个中间缓冲区。但如果需要,可以合并此缓冲区。

    另一种方法可能是编写您自己的循环,反复调用RetrieveResult,这样您就可以在 UI 更新后手动处理抓取结果。也可以保留一个 WriteableBitmaps 池,我想如果 UI 没有实际使用它应该是安全的。

    【讨论】:

    • “首先,BitmapImage 实际上是does not need disposing,它将像任何其他托管对象一样被 GC 收集” - 那是 not entirely corrrect 并且依赖于 它是如何构建的。休息很好+1
    【解决方案3】:

    我处于类似情况:从相机中提取实时图像并将其转储到 UI 以进行“实时”查看。我花了很多时间试图找到最有效的解决方案。对我来说,原来是 BitmapSource.Create。我采用原始字节数组(加上一个描述图像特征(如宽度、高度等)的结构)并使用一个函数调用将其转换为 BitmapSource。

    现在在我的例子中,图像是灰度的 8 位图像,所以如果你想显示颜色,你的论点会有所不同。但这是我所做的一个简单的介绍。

    public class XimeaCameraImage : ICameraImage
    {
        public unsafe XimeaCameraImage(byte[] imageData, ImgParams imageParams)
        {
            Data             = imageData;
            var fmt          = PixelFormats.Gray8;
            var width        = imageParams.GetWidth();
            var bitsPerPixel = 8;   // TODO: Get ready for other image formats
            var height       = imageParams.GetHeight();
            var stride       = (((bitsPerPixel * width) + 31) / 32) * 4;
            var dpi          = 96.0;
    
            // Copy the raw, unmanaged, image data from the Sdk.Image object.
    
            Source = BitmapSource.Create(
                width,
                height,
                dpi,
                dpi,
                fmt,
                BitmapPalettes.Gray256,
                imageData,
                stride);
    
            Source.Freeze();
        }
    
        public           byte[]        Data { get; }
        public           BitmapSource  Source            { get; }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-07-24
      • 1970-01-01
      • 1970-01-01
      • 2015-04-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多