在WPF中使用Images时出现OutOfMemoryException

时间:2016-05-01 18:39:28

标签: c# wpf bitmapimage imaging

我必须从大图像的特定部分制作一些缩略图,并将它们保存为png / jpeg图像。这就是我正在做的事情:

public static void Save(BitmapImage srcBitmap, Int32Rect srcRegion, Rect destRegion)
{
    var cropped = new CroppedBitmap(srcBitmap, srcRegion);

    var drawingVisual = new DrawingVisual();

    using (DrawingContext drawingContext = drawingVisual.RenderOpen())
    {
        drawingContext.DrawImage(cropped, destRegion);
    }

    var bmp = new RenderTargetBitmap(256, 256, 96, 96, PixelFormats.Pbgra32);

    bmp.Render(drawingVisual);

    var bitmapEncoder = new PngBitmapEncoder();

    bitmapEncoder.Frames.Add(BitmapFrame.Create(bmp));

    using (var filestream = new FileStream(path, FileMode.Create))
    {
        bitmapEncoder.Save(filestream);
    }
}

我可以使用具有不同Bitmap的单个大srcRegion来调用此方法一千次,应用程序越来越多地使用RAM并最终抛出System.OutOfMemoryException!似乎这个功能有内存泄漏,但我不知道它在哪里。有人可以帮忙吗?

编辑:我也不确定这是获取大图像的一部分并将该部分调整为较小的图像(例如256 * 256)并保存它的最佳方法。有没有更好的主意?

enter image description here

2 个答案:

答案 0 :(得分:1)

RenderTargetBitmap不是IDisposable,但应该是(因为它使用原生资源),这是一个奇怪的设计实现,但事实就是如此。您可以尝试在退出之前调用bmp.Clear(),这应该会释放本机资源。

RenderTargetBitmap自己检查GC压力(使用SafeMILHandle)来释放非托管资源,但根据我的经验,效果不是很好(这是很久以前的事了,这些天的事情可能会更新)

此外,不是为了生产代码,而是为了测试目的,我添加了一个:

GC.Collect();
GC.WaitForPendingFinalizers();

在你的方法之上(或者在调用它之后,从调用者那里),只是为了确保问题不是托管资源,而GC没有内存压力来释放它们(你可能有更多的内存可用)系统比你可以持有本机句柄,这将使它全部混淆。)

答案 1 :(得分:0)

好的,我使用TransformedBitmap改进了这段代码(实际上我从@Clemens中得到了这个想法)并且不再抛出任何异常。

public static void Save(BitmapImage srcBitmap, Int32Rect srcRegion, Rect destRegion)
{
    var cropped = new CroppedBitmap(srcBitmap, srcRegion);

    var drawingVisual = new DrawingVisual();

    //Here is the changes:
    var scale = 256.0 / srcRegion.Width;
    var transform = new ScaleTransform(scale, scale);
    //

    using (DrawingContext drawingContext = drawingVisual.RenderOpen())
    {
        //Here is the changes:
        drawingContext.DrawImage(new TransformedBitmap(cropped, transform), destRegion);
        //
    }

    var bmp = new RenderTargetBitmap(256, 256, 96, 96, PixelFormats.Pbgra32);

    bmp.Render(drawingVisual);

    var bitmapEncoder = new PngBitmapEncoder();

    bitmapEncoder.Frames.Add(BitmapFrame.Create(bmp));

    using (var filestream = new FileStream(path, FileMode.Create))
    {
        bitmapEncoder.Save(filestream);
    }
}