开发者

WPF Image Processing - Memory is never released

I am writing an app to read in a very large tif file (15000x10000), then chop it up into 256x256 tiles which get saved as jpegs. I am trying to use the WPF windows.media.imaging objects to do the chopping. The example below runs fine and will extract a number of tiles for me (it just gets multiple copies of the same tile for this example) but the app uses up memory and that memory never gets released. Even forcing a CG.Collect to test this still doesn't free up the memory.

 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

Am I missing something fundamental here? How can I ensure that these resources are released correctly?

Thanks

More Information:

The answers provided below definitely help. Thanks for that. I have a another issue to add to this now. I am trying to watermark each tile before it is save by adding the following code:

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)

This is starting to use some serious memory. Running through the large tif uses over 1200MB of memory as reported by task manager. It looks like this memory gets released eventually, but I am slightly concerned that something is not right with the code and is there anyway to stop it consuming all this memory in the first place. Perhaps this is simply down to the issue that Franci discussed?

Andrew


Your large object is definitely promoted to gen 2, after the ~25000 object allocations in the loop. Gen 2 objects are collected only on full collections, which are done rarely, thus your object might sit in memory for a while.

You can try forcing a full collection using GC.Collect(). You can also use the other GC methods to check for the allocated memory before you force the full allocation, then wait for it to finish and then check the allocated memory again to determine if indeed the large object was collected.

Note that even though a full collection might occur, the memory has already been included in the process working set, and it might take a bit for the OS to react to freeing that memory and remove it from the working set. This is important to keep in mind if you are trying to judge if the memory was properly collect by looking at the process working set in Task Manager.


I recreated your code locally and did a small experimentation, as a result I found that if you make the stream local to the for loop and use the enc.Save inside Using stream As New FileStream(System.IO.Path.Combine(outputDir, i.ToString() & ".jpg"), FileMode.Create, FileAccess.Write) the total private bytes stays approximately at 60 MB (against 80+ MB when not used inside using).

My complete code for your reference:

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


try creating your TiffBitmaDecoder like so:

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

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜