如何在不占用大量RAM的情况下显示图像

时间:2010-03-15 23:47:24

标签: c# .net silverlight silverlight-3.0

我正在开发一个Silverlight项目,用户可以在其中创建自己的拼贴。

问题

当使用BitmapImage类加载一堆图像时,Silverlight会占用大量不合理的RAM。 150张图片,其中单个填充最多4,5mb占用大约1,6GB的RAM - 从而最终导致内存异常。

我正在通过流加载它们,因为用户选择了自己的照片。

我在寻找什么

消除大量RAM被吸收的类,方法或一些过程。速度是一个问题,所以我不想在图像格式或类似的东西之间进行转换。快速调整大小的解决方案可能会有效。

我尝试使用WriteableBitmap渲染图像,但我发现这种方法迫使我在拖放和其他我希望用户能够处理图像的事情上重新发明轮子。 / p>

6 个答案:

答案 0 :(得分:6)

我要尝试的是在加载下一个流之前加载每个流并将其调整为缩略图(例如,640x480)。然后让用户使用较小的图像。准备好生成PDF后,一次一个地从原始流重新加载JPEG,在加载下一个位图之前处理掉每个位图。

答案 1 :(得分:4)

我猜你正在做点什么:

Bitmap bitmap = new Bitmap (filename of jpeg);

然后做:

OnPaint (...)
{
   Graphics g = ....;
   g.DrawImage (bitmap, ...);
}

每次绘制时,都会将巨大的JPEG图像调整为屏幕上显示的大小。我猜你的JPEG大小约为2500x2000像素。当您将JPEG加载到位图中时,位图加载代码会解压缩数据并将其作为RGB数据存储为易于渲染的格式(即与显示器采用相同的像素格式)或称为设备独立位图(又名DIBitmap)。这些位图需要比压缩JPEG更多的RAM存储。

您当前的实现已经在进行格式转换和调整大小,但是以一种无用的方式执行,即每次渲染时将大图像调整为屏幕大小。

理想情况下,您希望加载图像并将其缩小。 .Net有一个系统来做到这一点:

Bitmap bitmap = new Bitmap (filename of JPEG);
Bitmap thumbnail = bitmap.GetThumbnailImage (width, height, ....);
bitmap.Dispose (); // this releases all the unmanged resources and makes the bitmap unusable - you may have been missing this step
bitmap = null; // let the GC know the object is no longer needed

其中width和height是所需缩略图的大小。现在,这可能会产生看起来不像你想要的那样好的图像(但它会使用任何嵌入的缩略图数据,如果它存在,所以它会更快),在这种情况下,做一个位图 - >位图调整大小。

创建PDF文件时,您需要重新加载JPEG数据,但从用户的角度来看,这没关系。我确信用户不会介意等待一段时间将数据导出为PDF,只要你有一些反馈让用户知道它正在被处理。您也可以在后台线程中执行此操作,并让用户处理另一个拼贴。

答案 2 :(得分:1)

可能发生的事情是关于垃圾收集的一个鲜为人知的事实。如果一个对象足够大(我不记得该行的确切位置)垃圾收集将决定即使当前在范围内没有任何东西链接到该对象(在你的和我的对象都是图像)它保留图像在内存中,因为它已经决定,如果你再次想要那个图像,保持它而不是删除它并在以后重新加载它会更便宜。

答案 3 :(得分:1)

这不是一个完整的解决方案,但如果您要在位图和JPEG之间进行转换(反之亦然),则需要查看FJCore image library。它使用起来相当简单,并允许您执行调整JPEG图像大小或将其移动到不同质量的操作。如果您使用Silverlight进行客户端图像处理,那么这个库可能还不够,但肯定是必要的。

您还应该了解如何向用户展示图像。如果您正在使用Silverlight进行拼贴,可能您将无法使用虚拟化控件,因为用户将同时处理所有150个图像。但正如其他人所说,你也应该确保你没有基于全尺寸JPEG文件呈现位图。一个1MB的压缩JPEG可能会扩展到一个10MB的Bitmap,这很可能是你遇到很多麻烦的地方。确保您使用更小(质量更低,尺寸更小)的JPEG文件将您呈现的图像基于用户。

答案 4 :(得分:0)

最终为我工作的解决方案是使用WriteableBitmapEX执行以下操作:

当然,如果图像的尺寸不足以存储在内存中,我只会使用缩略图。

我得到的是,WriteableBitmap没有无参数构造函数,但是以0,0作为大小初始化它,然后加载源自动设置它们。这对我来说并不自然。

感谢大家的帮助!

private WriteableBitmap getThumbnailFromBitmapStream(Stream bitmapStream, PhotoFrame photoFrame)
    {
        WriteableBitmap inputBitmap = new WriteableBitmap(0,0);
        inputBitmap.SetSource(bitmapStream);

        Size thumbnailSize = getThumbnailSizeFromWriteableBitmap(inputBitmap, photoFrame.size);

        WriteableBitmap thumbnail = new WriteableBitmap(0,0);
        thumbnail = inputBitmap.Resize((int)thumbnailSize.Width, (int)thumbnailSize.Height, WriteableBitmapExtensions.Interpolation.NearestNeighbor);

        return thumbnail;
    }

答案 5 :(得分:0)

使用以下方法减少ram的另一个变体: 不要加载此时不可见的图像,并在用户滚动页面时加载它们。 Web开发人员使用此方法来提高页面加载速度。对于你来说,它不是在ram中存储孔数量的方式。

而且我认为更好的方法是不要在运行时制作缩略图,而是将它们存储在全尺寸图片附近并获取它们的链接。当它需要时,你总是可以获得全尺寸图片的链接并加载它。