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)
精彩评论