.net之重新使用位图解码 JPEG 数据

mayingbao 阅读:72 2024-05-22 17:00:29 评论:0

我的应用程序的这一部分在图片框中显示来自网络摄像机的实时图像流。该流是典型的运动 JPEG,格式为多部分 HTTP 响应。

所有代码都已编写,我的应用程序运行良好,CPU 使用率非常低 (1-2%),内存占用量呈锯齿状(逐渐增加,直到触发 GC,然后全部回收恢复正常)。所以这更像是一个优化问题、最佳实践等,不是必需的。

除此之外,现在我正在以正常的 Bitmap.FromStream() 方式执行此操作,它以 60Hz 的频率为每一帧生成一个全新的位图。即使从运行时的角度来看它很好,但作为程序员,这对我来说是不和谐的。

如果我要使用更实际的方法,我会预先分配已知大小(320x240 或 640x480,取决于选项)的位图,然后将流解码到我的位图中,但我没有看到 .NET执行此操作的功能。我必须使用自己的 JPEG 解码器,这既工作量太大,二进制大小也更大(这已经是 55mb 的编译代码)。

所以我的问题是,我在这里遗漏了什么吗?有没有最好的方法来做这样的事情?我提到的功能会很完美,但如果不可用,我还能如何改进它?

请您参考如下方法:

请记住,从 60 fps jpeg 获得 2% 的 CPU 负载是非常的好结果。你几乎无能为力使它变得更好。请确保您的基准测试是真实的,当它变得如此低时,您不能对位图数据做太多事情。一个潜在的陷阱是编解码器延迟转换压缩像素数据。如果您实际上从未调用过 Graphics.DrawImage() 或 Bitmap.LockBits(),那么编解码器除了解析 header 之外什么都不做。您获得的 .NET 对象也非常适中,仅需要 20 字节的 GC 堆。

没有理由担心底层内存管理。它在 GDI+ 中是非常不透明的,它完全自己处理它,除了一个指针之外没有暴露任何东西。 Image.FromFile() 远远优于 GDI+ 使用内存映射文件访问原始像素数据。当然,在相机应用程序中没有用,由于 GC 和 native 内存之间的阻抗不匹配,Image.FromStream() 涉及更多,引擎盖下有一个 Marshal.Copy() 来铲字节。编解码器可能会将自己分配给缓冲/缓存解码像素,您对此无能为力。没有理由担心这一点,因为您重复使用完全相同的分配,Windows 内存管理器非常喜欢这一点。

您所要求的可能的。两种基本方法。第一个是 Bitmap constructor它需要一个 IntPtr,它需要指向解码后的像素数据。这使您可以重新使用原始像素缓冲区。第二种方式是Bitmap.LockBits() ,你可以直接涂写到GDI+分配的像素缓冲区中。两者之间没有根本区别。

当然,您需要携带自己的 JPEG 解码器。 libjpeg library是最常用的一种。用 C 编写,最好的合并方式是使用 C++/CLI 包装器。 libjpeg-turbo 库是值得注意的,它有望将解码速度提高 2-4 倍。不知道这些库与 Microsoft 的编解码器相比如何。请注意您解码的像素格式,24bppRgb 是一种自然的 JPEG 匹配,因此很可能是您从编解码器中得到的格式,但如果您显示图像,32bppPArgb 是目前最好的格式 (x10)。


标签:程序员
声明

1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。

关注我们

一个IT知识分享的公众号