我的应用程序的这一部分显示来自图片框中IP摄像机的实时图像流。流是典型的动态JPEG,格式化为多部分HTTP响应。
所有代码都已编写,我的应用程序正常工作,CPU使用率非常低(1-2%)和跷跷板内存占用(逐渐增加,直到GC被触发,然后它们全部回收再恢复正常)。所以这更像是一个优化问题,最好的实践等,它不是必需的。
有了这个,现在我正在以正常的Bitmap.FromStream()
方式进行此操作,它为每个帧生成一个全新的位图,频率为60Hz。即使从运行时的角度来看它很好,但对于我来说,这对程序员来说也很不利。
如果我要使用更多动手方法,我会预先分配已知大小的位图(320x240或640x480,具体取决于选项)并将流解码为我的位图,但我没有看到.NET这样做的功能。我必须使用我自己的JPEG解码器,它既有太多的工作,也有更多的二进制大小(这个已经是55mb的编译代码)。
所以我的问题是,我在这里遗漏了什么吗?有没有最好的办法做这样的事情?我提到的功能是完美的,但如果没有,我还能如何改进呢?
答案 0 :(得分:3)
请记住,从60 fps jpegs获得2%的CPU负载是一个非常的好结果。你可以做的很少,以使这更好。确保您的基准测试是真实的,当位图数据变得很低时,您无法对位图数据做很多事情。潜在的陷阱是编解码器懒惰地转换压缩的像素数据。如果您实际上从未调用Graphics.DrawImage()或Bitmap.LockBits(),则编解码器不会执行任何操作,而是解析标头。您获得的.NET对象也非常适度,只需要20个字节的GC堆。
没有理由担心底层的内存管理。它在GDI +中非常不透明,它完全可以自己处理它而不会暴露任何东西而不是指针。 Image.FromFile()是最优秀的,GDI +使用内存映射文件来访问原始像素数据。当然,在相机应用程序中没有用它,由于GC和本机内存之间的阻抗不匹配,Image.FromStream()更加复杂,在引擎盖下有一个Marshal.Copy()来铲除字节。编解码器很可能将自己分配给缓冲/缓存解码的像素,你无法控制它。没有理由担心,因为你反复使用完全相同的分配,Windows内存管理器非常喜欢。
您要求的 是可能的。两种基本方法。第一个是采用IntPtr的Bitmap constructor,它需要指向解码的像素数据。这让您可以重复使用原始像素缓冲区。第二种方式是Bitmap.LockBits(),你可以直接涂鸦到GDI +分配的像素缓冲区。两者之间没有根本区别。
当你这样做时,你需要带上你自己的JPEG解码器。 libjpeg library是最常用的一种。用C语言编写,最好的方法是使用C ++ / CLI包装器。 libjpeg-turbo库值得注意,有望提高2-4倍的解码速度。不知道这些库与微软的编解码器相比如何。注意你解码的像素格式,24bppRgb是一个自然的JPEG匹配,所以很可能你会得到一个编解码器,但如果你显示图像,32bppPArgb是最好的格式(x10)。
答案 1 :(得分:0)
首先,你可以尝试使用WPF ,它的图片组件&渲染能力。 AFAIK,有几个提到WPF Image类比WinForm有更好的性能,它的基于GDI +的类。
第二,如果您仍想使用WinForm 作为UI,如果您允许包含对Systems.Windows.Media.Imaging(PresentationCore.dll)的引用,那么您可以尝试使用JpegBitmapDecoder
类对图像进行解码,然后将解码后的像素设置为预先分配的Bitmap
实例。
JpegBitmapDecoder构造函数有几个选项,您可以调整(可能)更好的性能,即create-options和cache-options - > http://msdn.microsoft.com/en-us/library/ms602494.aspx
然后将BitmapFrame实例作为
BitmapFrame bitmapFrame = jpegBitmapDecoder.Frames[0];
BitmapFrame为CopyPixels method提供了多个重载。该方法可以复制到Array或IntPtr。我相信除了我之外的另一个答案已经提到IntPtr作为Bitmap中的原始像素缓冲区,因此您可以将BitmapFrame中的像素直接复制到预分配的Bitmap中。