【问题标题】:How to display images without taking up huge amounts of RAM如何在不占用大量 RAM 的情况下显示图像
【发布时间】:2010-03-15 23:47:23
【问题描述】:

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

问题

当使用 BitmapImage 类加载一堆图像时,Silverlight 会占用大量不合理的 RAM。 150 张图片,其中单张最多 4.5mb 占用大约 1.6GB 的 RAM - 因此最终引发内存异常。

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

我在寻找什么

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

我尝试使用 WriteableBitmap 将图像渲染到其中,但我发现这种方法迫使我在拖放和其他我希望用户能够对图像执行的操作方面重新发明轮子。

【问题讨论】:

  • 所以...您想要一个高效的解决方案,但您不想使用提高效率的方法,即在较低级别处理图像?为什么不?我不了解 Silverlight,希望您能找到解决方案,但有时您确实必须卷起袖子做一些工作。
  • 问题只是内存效率。我不需要快速渲染或快速修改图像数据本身——我只需要使用内存中的实际 JPEG 数据来表示它并稍后将其合并到一个 pdf 中。我使用的 PDF 框架要求我输入 JPEG 数据流,但我没有看到将 BitmapImage 转换为 JPEG 流的好方法。
  • 您能否提供一些关于您的程序如何工作的代码,以及您的图像的一般尺寸是多少?您一次显示多少个?

标签: c# .net silverlight silverlight-3.0


【解决方案1】:

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

【讨论】:

  • 这会将您的 4.5 MB 图像转换为 ~.9 MB 图像。通过使用 16 位颜色,您可以将其降低到 0.6 兆字节。现在您的 150 张图像大约需要 60 兆字节。仍然很多,但更合理。同样,只要您使用原始图像进行最终渲染,就可以了。
  • 我已经尝试过这样做,但它确实会影响处理能力。我认为silverlight在渲染图像时已经以某种方式做到了这一点。是否不能强制位图图像丢弃它们的资源,同时将最终渲染的图像保留在内存中?
  • WPF 至少将其作为合成过程的一部分。因此,缩放是由图形硬件即时完成的。我怀疑 silverlight 做了类似的事情。在这种情况下,答案是否定的,它还没有这样做,至少不是以您可以访问的方式。
【解决方案2】:

我猜你在做类似这样的事情:

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

其中宽度和高度是所需缩略图的大小。现在,这可能会产生看起来不像您希望的那样好的图像(但如果存在,它将使用任何嵌入的缩略图数据,因此它会更快),在这种情况下,请执行 bitmap->bitmap resize .

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

【讨论】:

  • 你是对的,但在 Silverlight 中,我使用派生自这些的类。我需要使用 BitmapImage,因为没有其他等价物。这意味着我无法直接控制格式转换,也没有任何方法可以从 jpeg 中获取缩略图。所以我唯一的选择是从原始图像中制作一个位图图像,然后使用它来创建一个缩略图——但是,我遇到了上面 Alex 指出的问题。感谢您的回复。
  • @Sheeo:我认为 Image.Dispose 将解决 Alex 提出的问题,因为您告诉系统您不再需要这些资源 - 文档指出 Dispose 方法会释放所有未管理的资源(其中大部分将是非托管的 - DIBitmap)
【解决方案3】:

可能发生在你身上的是一个鲜为人知的关于垃圾收集的事实,这也让我感到震惊。如果一个对象足够大(我不记得这条线的确切位置)垃圾收集将决定即使当前范围内的任何内容都没有链接到该对象(在您和我的对象中都是图像)它会保留图像在内存中,因为它已经决定,如果您再次需要该图像,保留它而不是删除它并稍后重新加载它更便宜。

【讨论】:

  • 是的,我在四处探听答案时发现了这个问题。目前没有解决方法,还是什么?
  • System.GC.Collect() 和 System.GC.WaitForPendingFinalizers() 似乎是我帮助它的方法,但可能有更好的方法。
【解决方案4】:

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

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

【讨论】:

  • 感谢您的回答,我也偶然发现了 FJCore——它有很多问题,例如,与通过 BitmapImage 加载相比,jpeg 在解码时实际上变得更大。上面讨论了您答案的底部。
【解决方案5】:

最终对我有用的解决方案是使用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;
    }

【讨论】:

    【解决方案6】:

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

    而且我认为最好不要在运行时制作缩略图,而是将它们存储在全尺寸图片附近并仅获取它们的链接。需要时,您始终可以获取全尺寸图片的链接并加载它。

    【讨论】:

      猜你喜欢
      • 2019-10-26
      • 1970-01-01
      • 2011-12-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-16
      • 2010-11-18
      相关资源
      最近更新 更多