Обработка изображений WPF - память никогда не освобождается - PullRequest
2 голосов
/ 05 ноября 2010

Я пишу приложение для чтения в очень большом TIF-файле (15000x10000), а затем делю его на фрагменты размером 256x256, которые сохраняются в формате JPEG.Я пытаюсь использовать объекты WPF windows.media.imaging для измельчения.Приведенный ниже пример работает нормально и извлекает для меня несколько плиток (для этого примера он получает несколько копий одной и той же плитки), но приложение использует память, и эта память никогда не освобождается.Даже принудительное тестирование CG.Collect по-прежнему не освобождает память.

 Dim croppedImage As CroppedBitmap

 Dim strImagePath As String = "C:\Huge.tif"

  Dim imageSource As BitmapSource = TiffBitmapDecoder.Create(New Uri(strImagePath), BitmapCreateOptions.IgnoreImageCache, BitmapCacheOption.None).Frames(0) 'CreateImage(imageBytes, 0, 0)
  Dim enc As JpegBitmapEncoder
  Dim stream As FileStream

  For i As Integer = 0 To 5000
      croppedImage = New CroppedBitmap()

      croppedImage.BeginInit()
      croppedImage.Source = imageSource
      croppedImage.SourceRect = New Int32Rect(0, 0, 256, 256)
      croppedImage.EndInit()

      enc = New JpegBitmapEncoder()
      enc.QualityLevel = 70
      enc.Frames.Add(BitmapFrame.Create(croppedImage))
      stream = New FileStream("C:\output\" & i & ".jpg", FileMode.Create, FileAccess.Write)
      enc.Save(stream)
      stream.Close()

      enc = Nothing
      stream = Nothing
      croppedImage.Source = Nothing
      croppedImage = Nothing

  Next

  imageSource = Nothing

Я что-то упустил здесь?Как я могу обеспечить правильное освобождение этих ресурсов?

Спасибо

Дополнительная информация:

Ответы, приведенные ниже, определенно помогут.Спасибо за это.У меня есть еще одна проблема, чтобы добавить к этому сейчас.Я пытаюсь сделать водяной знак на каждой плитке перед ее сохранением, добавив следующий код:

Dim targetVisual = New DrawingVisual()
Dim targetContext = targetVisual.RenderOpen()

targetContext.DrawImage(croppedImage, New Rect(0, 0, tileWidth, tileHeight))
targetContext.DrawImage(watermarkSource, New Rect(0, 0, 256, 256))

Dim target = New RenderTargetBitmap(tileWidth, tileHeight, 96, 96, PixelFormats.[Default])

targetContext.Close()
target.Render(targetVisual)
Dim targetFrame = BitmapFrame.Create(target)

Это начинает использовать серьезную память.При работе с большим TIF используется более 1200 МБ памяти, как сообщает диспетчер задач.Похоже, что эта память в конце концов освобождается, но я немного обеспокоен тем, что с кодом что-то не так, и есть ли возможность остановить его, потребляя всю эту память в первую очередь.Возможно, это просто вопрос, который обсуждал Фрэнси?

Андрей

Ответы [ 3 ]

0 голосов
/ 05 ноября 2010

Я пересоздал ваш код локально и провел небольшой эксперимент. В результате я обнаружил, что если вы сделаете stream локальным для цикла for и используете enc.Save внутри Using stream As New FileStream(System.IO.Path.Combine(outputDir, i.ToString() & ".jpg"), FileMode.Create, FileAccess.Write), то общее количество приватных байтов останется примерно равным 60МБ (против 80+ МБ, когда не используется внутри using).

Мой полный код для справки:

Dim fileName As String = System.IO.Path.Combine(Environment.CurrentDirectory, "Image1.tif")
Dim outputDir As String = System.IO.Path.Combine(Environment.CurrentDirectory, "Out")

Dim imageSource As BitmapSource = TiffBitmapDecoder.Create(New Uri(fileName), BitmapCreateOptions.IgnoreImageCache, BitmapCacheOption.None).Frames(0)

Dim enc As JpegBitmapEncoder
Dim croppedImage As CroppedBitmap

For i As Integer = 0 To 4999
    croppedImage = New CroppedBitmap()

    croppedImage.BeginInit()
    croppedImage.Source = imageSource
    croppedImage.SourceRect = New Int32Rect(0, 0, 256, 256)
    croppedImage.EndInit()

    enc = New JpegBitmapEncoder()
    enc.QualityLevel = 70
    enc.Frames.Add(BitmapFrame.Create(croppedImage))
    Using stream As New FileStream(System.IO.Path.Combine(outputDir, i.ToString() & ".jpg"), FileMode.Create, FileAccess.Write)
        enc.Save(stream)
    End Using

    enc = Nothing
    croppedImage.Source = Nothing

    croppedImage = Nothing
Next

imageSource = Nothing
0 голосов
/ 09 июня 2015

попробуйте создать свой TiffBitmaDecoder следующим образом:

Dim imageSource As BitmapSource = TiffBitmapDecoder.Create(New Uri(fileName), BitmapCreateOptions.IgnoreImageCache, BitmapCacheOption.OnLoad).Frames(0)
0 голосов
/ 05 ноября 2010

Ваш большой объект определенно повышен до поколения 2, после ~ 25000 выделений объектов в цикле. Объекты Gen 2 собираются только в полных коллекциях, что делается редко, поэтому ваш объект может некоторое время находиться в памяти.

Вы можете попытаться форсировать полную коллекцию, используя GC.Collect(). Вы также можете использовать другие методы GC для проверки выделенной памяти, прежде чем принудительно завершить выделение памяти, затем дождаться ее завершения и затем снова проверить выделенную память, чтобы определить, действительно ли был собран большой объект.

Обратите внимание, что, хотя может произойти полный сбор, память уже включена в рабочий набор процесса, и ОС может потребоваться некоторое время, чтобы отреагировать на освобождение этой памяти и удалить ее из рабочего набора. Это важно иметь в виду, если вы пытаетесь оценить, правильно ли был собран объем памяти, посмотрев на рабочий набор процессов в диспетчере задач.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...