TiffBitmapEncoder, ошибка памяти, приводящая к недостаточному исключению памяти в C#/WPF

У меня есть приложение WPF, где я сохраняю пару сотен битмап-источников, конвертируя их в изображения TIFF с помощью TiffBitmapEncoder. Тем не менее, у меня есть это странное потребление памяти, которое часто вызывает исключение недостаточного объема памяти.

НОТА:

  • У меня установлено 8 ГБ оперативной памяти.
  • Размеры изображения варьируются от 10x10 до 300x300 пикселей (довольно маленькие)

Вот код, который работает:

static void SaveBitmapSource(BitmapSource bitmapSource)
{
    TiffBitmapEncoder encoder = new TiffBitmapEncoder();
    encoder.Compression = TiffCompressOption.Zip;
    BitmapFrame frame = BitmapFrame.Create(bitmapSource);

    encoder.Frames.Add(frame);

    using (MemoryStream ms = new MemoryStream())
    {
        encoder.Save(ms);
    }
}

И вот скриншот моей памяти:

Нет исключения памяти

Теперь, если я клонирую BitmapSource (хотя бы один раз), я получаю такое огромное выделение памяти, что вызывает исключение "Недостаточно памяти".

static BitmapSource source2 = null;
static void SaveBitmapSource(BitmapSource bitmapSource)
{
    if (source2 == null)
    {
        source2 = bitmapSource.Clone();
    }
    TiffBitmapEncoder encoder = new TiffBitmapEncoder();
    encoder.Compression = TiffCompressOption.Zip;
    BitmapFrame frame = BitmapFrame.Create(source2);

    encoder.Frames.Add(frame);

    using (MemoryStream ms = new MemoryStream())
    {
        encoder.Save(ms);
    }
}

Вот скриншот моей памяти для второго примера кода

Исключение памяти

Кто-нибудь знает, что может быть причиной и как это исправить?

Разница в том, что BitmapSource в первом примере отображался на экране, а во втором - нет. Я подозреваю, что это может быть как-то связано с GPU и Dispatcher, которые могут быть аппаратными средствами, ускоряющими конвертацию, тогда как второе делается на CPU, где есть какая-то ошибка...

Пытался:

  • Пробовал звонить GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true); после SaveBitmapSource() без удачи

3 ответа

Вы должны использовать файловый поток, чтобы уменьшить использование памяти;

        BitmapDecoder decoder;
        using (Stream appendToOutput = File.Open(files[0], FileMode.Open, FileAccess.Read, FileShare.Read))
        {
            decoder = BitmapDecoder.Create(appendToOutput, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.None);
            using (Stream output = File.Open(outputFile, FileMode.Create, FileAccess.Write))
            {
                TiffBitmapEncoder childEncoder = new TiffBitmapEncoder();
                if(Path.GetExtension(files[0]).Replace(".", "") == ScanningImageFormat.Jpeg) {
                    childEncoder.Compression = TiffCompressOption.Zip;
                } else {
                    childEncoder.Compression = TiffCompressOption.Ccitt4;
                }

                foreach (BitmapFrame frm in decoder.Frames)
                {
                    childEncoder.Frames.Add(frm);
                }

                List<Stream> imageStreams = new List<Stream>();
                try
                {
                    for (int i = 1; i < files.Count; i++)
                    {
                        string sFile = files[i];
                        BitmapFrame bmp = null;
                        Stream original = File.Open(sFile, FileMode.Open, FileAccess.Read);
                        imageStreams.Add(original);
                        bmp = BitmapFrame.Create(original, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.None);
                        childEncoder.Frames.Add(bmp);
                    }
                    childEncoder.Save(output);
                }
                finally
                {
                    try
                    {
                        foreach (Stream s in imageStreams)
                        {
                            s.Close();
                        }
                    }
                    catch { }
                }
            }
        }
        decoder = null;

Я решил проблему, позвонив

GC.WaitForPendingFinalizers();

сразу после SaveBitmapSource(),

Так что я думаю, что внутри есть какой-то неуправляемый ресурс BitmapSource и / или BitmapEncoder который не был выпущен, пока не были запущены методы Finalize...

Я рад, что это решает проблему. Но я не уверен, что это правильное решение. Я вижу, что вы вызываете BitmapFrame.Create() только с одним параметром. Вы можете посмотреть на это более внимательно..

Попробуйте использовать флаг BitmapCacheOption.None - по умолчанию он может кэшировать каждое растровое изображение в памяти без причины:

BitmapFrame.Create (source, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.None);

Другие вопросы по тегам