在 WPF 中处理图像时出现 OutOfMemoryException

OutOfMemoryException while working with Images in WPF

我必须从大图像的特定部分制作一些缩略图并将它们保存为 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);
    }
}

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

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

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

RenderTargetBitmap 自己检查 GC 压力(使用 SafeMILHandle)以释放非托管资源,但根据我的经验,效果不是很好(这是很长一段时间不过,这几天可能会更新一些东西)

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

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

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

好的,我通过使用 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);
    }
}