2010-11-05 54 views
2

我正在写一个应用程序来读取一个非常大的tif文件(15000x10000),然后将它切成256x256的拼贴图,并保存为jpeg。我正在尝试使用WPF windows.media.imaging对象来做斩波。下面的例子运行正常,并会为我抽取一些图块(它只是获得同一个图块的多个副本),但是应用程序使用了内存,并且内存永远不会被释放。即使强制一个CG.Collect来测试,这仍然不能释放内存。WPF图像处理 - 内存永远不会释放

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时使用了超过1200MB的内存。看起来这个内存最终会被释放,但是我略微担心代码中的某些内容不正确,并且无论如何阻止它首先消耗所有这些内存。也许这只是归结于Franci所讨论的问题?

安德鲁

回答

0

你的大对象肯定是提升到第2代,在循环中的〜25000次对象分配后。 Gen 2对象只在完整的集合上收集,很少完成,因此您的对象可能会在内存中驻留一段时间。

您可以尝试使用GC.Collect()强制完整收集。您也可以在强制完成分配之前使用其他GC方法检查分配的内存,然后等待它完成,然后再次检查分配的内存以确定是否确实收集了大型对象。

请注意,即使可能发生完全集合,内存已包含在进程工作集中,并且操作系统可能需要一点时间才能释放该内存并将其从工作集中删除。如果要通过查看“任务管理器”中设置的进程工作集来判断内存是否正确收集,请务必记住这一点。

+0

感谢您的输入。我想我需要更多地了解垃圾收集器以及它是如何工作的。 – 2010-11-08 10:33:54

0

我重新本地代码,并做了一个小实验,结果我发现,如果你做出stream当地的for循环,并使用enc.SaveUsing stream As New FileStream(System.IO.Path.Combine(outputDir, i.ToString() & ".jpg"), FileMode.Create, FileAccess.Write)总数专用字节在60 MB大约保持(对80+ MB不在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

这当然有助于控制内存使用情况。不过,当我试图对水印进行水印处理时,我还有另一个问题。我已经将此添加到上面的问题。 – 2010-11-08 11:12:25

+0

使用(流...)只是调用stream.Dispose,它与流相同。关闭(除非抛出异常)。如果您发现内存使用情况有任何差异,那么您测量它的方式有点不妥。 – adrianm 2010-11-08 11:57:44

+0

@adrianm我正在使用sysinternals进程资源管理器专用字节分配检查内存分配。除此之外,我对enc.Save方法进行了调整,因为在通过vs2010性能分析工具进行分析时报告使用最大内存。请让我知道是否有更好的方法。谢谢。 – 2010-11-08 19:21:14

0

尝试创建TiffBitmaDecoder像这样:

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