在 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 中以高刷新率显示字节数组图像的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Windows Phone 8.1 上从数据库中获取图像并将其显示为字节数组