如何将图像从流中解码为现有图像

时间:2013-05-24 08:04:59

标签: c# wpf garbage-collection

我有一个与NAudio播放音频的应用程序。 NAudio的一个已知限制是,每次垃圾收集器运行时,每个线程都会暂停,直到完成为止。

应用程序运行正常,所有GC都在可接受的时间内完成,没有口吃。

但是我们还有一个单独的应用程序,它每秒通过TCP向主应用程序(带有音频播放器)发送缩略图。当编码为JPEG时,缩略图大约为1300字节。

这是我们目前用于解码图像的代码:

MemoryStream ms = new MemoryStream(data);
BitmapDecoder bdec = BitmapDecoder.Create(ms, BitmapCreateOptions.None, BitmapCacheOption.Default);
BitmapSource source = bdec.Frames[0];
imgPreview.Source = source;

并编码:

JpegBitmapEncoder jpgEncoder = new JpegBitmapEncoder();
jpgEncoder.QualityLevel = quality;
jpgEncoder.Frames.Add(BitmapFrame.Create(renderTarget));

byte[] imageArray;
using (MemoryStream outputStream = new MemoryStream())
{
    jpgEncoder.Save(outputStream);
    imageArray = outputStream.ToArray();
}

其中RenderTarget是具有图像内容的RenderTargetBitmap。

现在我们每秒都在创建并丢弃一个MemoryStream,一个BitmapDecoder和一个BitmapSource。我已经注释掉了代码中的行,看起来像MemoryStream和BitmapDecoder构造函数没有创建任何断言,但是一旦通过Frames [0]访问它,就会开始断断续续。

我们也尝试过这种方法而不是BitmapDecoder,但结果相同:

img.BeginInit();
img.StreamSource = ms;
img.EndInit();

当然有更好的方法可以持续更新图像吗?

最好的方法是只发送原始图像数据,然后创建一个WriteableBitmap,这些只是每秒重写一次。但是原始图像是170 kb,比编码图像多100多倍,我们真的不想这样做。是否可以将JPEG流解码为现有的字节数组或现有的图像?

2 个答案:

答案 0 :(得分:4)

好的,所以我找到了解决问题的方法。

我没有使用WPF BitmapDecoder来解码图像,而是使用Windows窗体位图。它是一次性的,对垃圾收集器来说更好。

所以解决方案如下:

var stream = new MemoryStream(data);
var formsBitmap = new Bitmap(stream);

var width = formsBitmap.Width;
var height = formsBitmap.Height;
if (bitmap == null || height != bitmap.PixelHeight || width != bitmap.PixelWidth)
{
    bitmap = new WriteableBitmap(width, height, 96, 96, PixelFormats.Pbgra32, null);
    imgPreview.Source = bitmap;
}

BitmapData data = formsBitmap.LockBits(new Rectangle(0, 0, formsBitmap.Width, formsBitmap.Height),
                                        ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);

try
{
    bitmap.WritePixels(new Int32Rect(0, 0, width, height), data.Scan0, data.Stride * data.Height, data.Stride);
}
finally
{
    formsBitmap.UnlockBits(data);
}

formsBitmap.Dispose();

不是解码我收到的每个JPEG帧,而是从数据中创建一个新的Bitmap(来自WinForms)。然后我只是将像素复制到我使用的WriteableBitmap。

希望这对其他人也有帮助。

答案 1 :(得分:1)

分析代码确认 bdec.Frames [0]; 需要相当大的CPU时间。看一下ILSpy中的代码,' Frames 'getter有一个空的实现(虚方法没有被JpgBitmapDecodersub类覆盖),所以我假设对底层的Windows API进行了某种调用在那里(?)

底线是解码JPG会比PNG或GIF慢;我会尝试PNG编码,因为它仍然应该提供良好的压缩比,但具有更好的性能。