2010-03-15 74 views
13

我正在开发一个silverlight项目,用户可以在其中创建自己的Collages。如何在不占用大量内存的情况下显示图像

问题

当使用的BitmapImage类加载大量的图片,Silverlight的猪了庞大的不合理数量的RAM。 150张照片,其中单张最多可容纳4,5mb,占用大约1,6GB内存 - 因此最终导致内存异常。

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

我正在寻找

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

我试过使用一个WriteableBitmap来渲染图像,但我发现这种方法迫使我重新发明轮子,当涉及到拖放和其他事情我希望用户能够处理图像。

+0

所以......你需要一个有效的解决方案,但是你不想使用提高效率的方法,即在较低的层次上处理图像?为什么不?我不知道Silverlight,希望你找到一个解决方案,但有时你实际上不得不卷起袖子做一些工作。 – 2010-03-15 23:58:53

+0

问题只在于内存效率。我不需要快速渲染或快速修改图像数据本身 - 我只需要使用不超过内存中的实际JPEG数据来表示它,并在稍后将它合并到PDF中。我使用的PDF框架需要我输入JPEG数据流,但我没有看到将BitmapImage转换为JPEG流的好方法。 – 2010-03-16 00:14:43

+1

您可以包含一些关于程序如何工作的代码,以及图像的一般大小是什么?您一次显示多少个? – 2010-04-06 14:31:08

回答

6

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

+0

这会将您的4.5兆字节图像变成〜.9兆字节的图像。你可以通过16位颜色将它降低到0.6兆字节。现在你的150张图片大约需要60兆字节。还有很多,但更合理。再次,只要你使用原始图像进行最终渲染,你会没事的。 – 2010-04-06 14:36:03

+0

我已经试过这样做了,它真的让处理能力陷入沉寂。 我认为Silverlight在某种程度上已经在做这件事情,当它呈现图片时。难以强制bitmapimages抛出资源,同时将最终渲染图像保存在内存中吗? – 2010-04-07 08:22:56

+0

至少,WPF将这作为合成过程的一部分。缩放因此由图形硬件实时完成。我怀疑silverlight会做类似的事情。在这种情况下,答案是否定的,它不是已经这样做了,至少不是以您可以访问的方式。 – 2010-04-12 00:33:50

1

可能发生在你身上的是一个关于垃圾收集的小知名事实,这也让我感觉良好。如果一个对象足够大(我不记得该行的确切位置),垃圾收集将决定即使当前没有作用域中的任何内容都与该对象相关联(在您和我的对象中都是图像),它会保留图像在内存中,因为它已经决定,如果你再次想要这个图像,保留它而不是删除它并稍后重新加载会更便宜。

+0

是的,我在探听答案时发现了这个问题。 现在还没有解决方法,或者是什么? – 2010-04-07 08:20:17

+0

System.GC.Collect()和System.GC.WaitForPendingFinalizers()是我似乎是如何帮助它的,但可能有更好的方法。 – Alex 2010-04-07 12:22:56

1

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

您还应该了解如何将图像呈现给用户。如果您正在使用Silverlight进行拼贴,估计您将无法使用虚拟化控件,因为用户将一次处理150张图片。但正如其他人所说的,您还应该确保您不会呈现基于全尺寸JPEG文件的位图。 1MB压缩的JPEG可能会扩展到10MB的位图,这可能是您的许多麻烦来自的地方。确保您以更小(低质量和调整大小)的JPEG文件为基础向用户呈现图像。

+0

感谢您的回答,我已经偶然发现了FJCore - 存在大量问题,例如,与通过BitmapImage加载它们时相比,解码时JPEG实际上会变得更大。 上面讨论了答案的底部。 – 2010-04-07 08:24:26

3

我猜你正在做的事情liek这样的:

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 

其中宽度和高度是所需缩略图的大小。现在,这可能会产生看起来不够好的图像(但它会使用任何嵌入的缩略图数据(如果存在的话会更快),在这种情况下,请执行位图 - >位图调整大小。

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

+1

你是对的,但是在Silverlight中,我使用了派生自这些类的类。我需要使用BitmapImage,因为没有其他的等价物。 这意味着我没有直接控制格式转换,并且我没有任何方法从jpeg获取缩略图。 所以我唯一的选择是从原始图像制作一个bitmapimage,并使用它来创建缩略图 - 然而,然后我遇到了上面Alex指出的问题。 谢谢你的回复。 – 2010-04-07 10:11:28

+0

@Sheeo:我认为Image.Dispose将解决Alex提出的问题,因为您告诉系统您不再需要这些资源 - 文档声明Dispose方法释放所有无法使用的资源(并且大部分资源将不受管理 - DIBitmap) – Skizz 2010-04-07 10:21:40

0

终于为我工作用WriteableBitmapEX做以下解决方案:

当然如果图像是不是已经足够小,存储在内存中,我只用缩略图。

gotch'a我有一个事实,即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; 
    } 
0

一个额外的变种,以减少RAM使用: 不要加载图像,此时AR无形的,而用户滚动页面加载它们。此方法由网页开发人员使用,以提高网页加载速度。对你来说,它不会在RAM中存储图像的孔数量。

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

相关问题