在 WPF 中以高刷新率显示字节数组图像

Posted

技术标签:

【中文标题】在 WPF 中以高刷新率显示字节数组图像【英文标题】:Show byte array image in WPF with high refresh rate 【发布时间】: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

【问题讨论】:

您遇到问题了吗?为什么你认为你不dispose it? 单个 WriteableBitmap 应该表现得更好。 我投票决定将此问题作为题外话结束,因为有关 代码优化 的问题是题外话。请参阅 Can I post questions about optimizing code on Stack Overflow?Is it okay to ask for code optimization help? 了解更多信息。 这能回答你的问题吗? Displaying a videostream from my webcam in WPF? 【参考方案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; 

【讨论】:

以上是关于在 WPF 中以高刷新率显示字节数组图像的主要内容,如果未能解决你的问题,请参考以下文章

ASP.Net MVC:如何显示模型中的字节数组图像

将字节数组转换/显示为 bmp/jpeg 图像

如何在 Windows Phone 8.1 上从数据库中获取图像并将其显示为字节数组

如何使用 C# 从字节数组中显示 Web 表单中的图像

在opencv中使用imshow显示字节数组的最快方法是啥?

将字节数组转换为图像并在 Razor 视图中显示