2016-05-01 5 views
1

Мне нужно сделать несколько миниатюр из определенных частей большого изображения и сохранить их в виде изображений png/jpeg. Вот что я делаю:OutOfMemoryException при работе с изображениями в WPF

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 в тысячу раз, приложение все больше потребляют больше памяти и, наконец, бросает System.OutOfMemoryException! Кажется, утечка памяти в этой функции, но я не знаю, где она. Кто-нибудь может помочь?

EDIT: Также я не уверен, что это лучший способ получить часть большого изображения и изменить размер этой части на меньшее изображение (например, 256 * 256) и сохранить его. Есть ли лучшая идея?

enter image description here

+1

Эта функция статична, что может быть причиной проблемы. Попробуйте удалить статический. Вы можете захотеть поместить код в отдельный класс, а затем удалить класс, чтобы обеспечить уничтожение объекта, чтобы предотвратить утечку. – jdweng

+0

Используйте PerfView и делайте память - сделайте снимок кучи. Что ты видишь? https://www.microsoft.com/en-us/download/details.aspx?id=28567. Возможно, что с .NET 4.6.2 все становится лучше, если утечка связана с слишком ленивым характером GC и странным WPF-способом обработки неуправляемых ресурсов. Это может представлять интерес: http://geekswithblogs.net/akraus1/archive/2016/04/14/174476.aspx –

+0

Является ли 'destRegion' когда-либо отличающимся от' (0, 0, 256, 256) 'и делает размер 'srcRegion' когда-либо отличается от' (256, 256) '? Если нет, возможно, вы можете напрямую передать CroppedBitmap в BitmapEncoder, не используя DrawingVisual и RenderTargetBtmap. – Clemens

ответ

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); 
    } 
} 
0

RenderTargetBitmap не IDisposable, но оно должно быть (так как она использует собственные ресурсы), это странно, реализация дизайн, но это, как это. Вы можете попытаться позвонить bmp.Clear() перед выходом, который должен выпустить собственные ресурсы.

RenderTargetBitmap выполняет собственную проверку на давление GC (с использованием SafeMILHandle), чтобы освободить неуправляемые ресурсы, но по моему опыту, который не работал очень хорошо (это было давно, но в наши дни все может быть обновлено)

Кроме того, не для производства кода, но для целей тестирования, я бы добавить:

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

в верхней части метода (или сразу после его вызова, от вызывающего абонента), просто чтобы сделать уверен, что проблема не управляется ресурсами, в то время как GC не имеет давления памяти, чтобы освободить их (возможно, у вас есть больше мнений y доступны в системе, чем вы можете удерживать собственные ручки, и это все запутает).

+0

bmp.Clear() не решила проблему. –

+0

Возможно, вам захочется принудительно собрать коллекцию GC (см. Примечание, которое я добавил). WPF действительно имеет странный способ обработки ресурсов и выполняет свои собственные проверки GC (вместо использования 'IDisposable', как и должно). GC часто не имеет достаточного давления для освобождения ресурсов, и неуправляемые ручки могут легко заполняться ... если это не работает, я могу только рекомендовать сделать еще более серьезное профилирование памяти: я не вижу ничего плохого в вашем коде – Jcl

Смежные вопросы