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