【问题标题】:OutOfMemory error while joining large images加入大图像时出现 OutOfMemory 错误
【发布时间】:2011-09-07 00:10:27
【问题描述】:

我正在使用下面的代码连接两个图像,但它会引发 OutOfMemory 错误,我的图像每个大约 1MB。

private Bitmap overlayMark(String first, String second)
{
    Bitmap bmp1, bmp2;
    bmp1 = BitmapFactory.decodeFile(first);
    bmp2 = BitmapFactory.decodeFile(second);
    if (bmp1 == null || bmp2 == null)
        return bmp1;

    int height = bmp1.getHeight();
    if (height < bmp2.getHeight())
        height = bmp2.getHeight();

    Bitmap bmOverlay = Bitmap.createBitmap(bmp1.getWidth() + bmp2.getWidth(), height,
            Bitmap.Config.ARGB_8888);// Out of memory
    Canvas canvas = new Canvas(bmOverlay);
    canvas.drawBitmap(bmp1, 0, 0, null);
    canvas.drawBitmap(bmp2, bmp1.getWidth(), 0, null);
    bmp1.recycle();
    bmp2.recycle();
    return bmOverlay;
}

更新:我尝试了以下两个答案,但仍然无法创建如此大尺寸的位图,问题是生成的位图在 2400x3200 左右尺寸太大,因此内存不足.

如何在不耗尽内存的情况下加入大图像?

【问题讨论】:

  • “内存不足”从何而来——BitmapFactory.decodeFile()?
  • 这一行位图 bmOverlay = Bitmap.createBitmap(bmp1.getWidth() + bmp2.getWidth(), height, Bitmap.Config.ARGB_8888);
  • 在创建新位图之前,为什么不压缩两个位图并尝试一下。这将更好地减少内存
  • @Andro_Selva 我不能这样做,因为要缩放图像,这样做会降低质量
  • 您可以尝试将 bmp 转换为 png 吗?然后它可以被压缩到更小的尺寸,但仍然不会失去任何质量。然后,如果 Java API 对此很聪明,并且允许您仍然访问每个像素的 RGB 颜色值而无需将其实际扩展为 BMP 数组,那么您可以手动构建一个 BMP 文件并将其流式传输到文件中。这样,您可以一次将所需的所有数据存储在内存中,并且一次可以缓冲和流出生成的图像几 KB。

标签: android bitmap out-of-memory


【解决方案1】:

我使用这个简单的经验法则: 繁重的工作(内存/CPU)在服务器上完成。

所以编写一些 servlet 来获取图像,将其调整为指定的尺寸(可能也会降低像素深度)并返回结果。

小菜一碟,它适用于您需要的任何移动设备。

祝你好运!

【讨论】:

  • 我知道所有好的做法,并且大部分时间都遵循它们,但客户不想调整图像大小或降低像素质量
  • 好了,发送到服务器后不需要删除原件。只需制作“缩略图”,将其发回并以与在 android 设备上创建调整大小的图像相同的方式使用它。重点是:只需使用服务器来承载任务。
  • 对不起,如果我不清楚我不想要任何缩略图我只想加入两个大图像,如果我在服务器端这样做,那么当我尝试显示此图像时它也会 OOM
  • 我了解您的需求,但加入服务器上的图像,但还要创建要显示的小图像(适当调整大小),创建小图像只是为了防止 OOM 正好解决了这个问题。大的是给你的客户的。
  • 实际上该应用程序只是要通过我的应用程序向用户显示图像,用户无法在我的应用程序之外查看图像,并且在我的应用程序中图像可以放大/缩小,因此我无法调整大小
【解决方案2】:

我认为像 Sumon 建议的解决方案可能会奏效。

  • 算出最终的大小 图像基于适合的内容 屏幕。
  • 使用获取第一张图片的大小 inJustDecodeBounds 技术。 弄清楚第一个的大小 最终图像中的图像。计算 调整参数大小。
  • 调整图像大小,加载到内存中。
  • 将调整大小的图像写回磁盘。 回收位图。 (这将有助于 调整第二张图片的大小时)
  • 重复第二张图片,只有你 可以跳过写入磁盘部分。
  • 加载第一张图片。

如果您只需要显示,那么就这样做。如果没有,那么您可以在此时组合成一个位图并写入磁盘。如果是这种情况,可能会很困难,因为内存中的屏幕大小基本上是 2 倍。在这种情况下,我建议将大小调整得更小。如果你不能变小,那么你将不得不走 NDK 路线,我想我不确定how much that will help。这是 NDK 和 JNI 的amusing intro。最后,我强烈建议使用运行 Android 2.3+ 的手机进行开发,因为它使用堆分配位图将使调试更加容易。更多关于here的信息。

【讨论】:

  • 首先我根本不需要调整图像的大小,除了调整大小之外,我已经实现了他的方法但没有用,当我创建一个等于大小的空位图时在那一行的两个位图我都得到了 OOM,我也在尝试使用 NDK,但我不知道 C 任何链接可能会有帮助
【解决方案3】:

如果您需要返回巨大的 2400x3200 位图作为结果,则无法真正实现这一目标。原因是 2400*3200*4 字节 ~ 30 Mb!当您甚至无法将返回值放入有限的堆空间(即 16Mb)时,您如何希望实现此方法?

即使您使用 16 位颜色,它仍然会失败,因为您最终会使用大约 15MB,这不会为语言运行时留下足够的空间。

【讨论】:

  • 我同意你的观点,但我不明白为什么 android 设备配备这样的高分辨率相机,如果它们不支持应用程序开发,它们会产生 3-5 MB 的图像
  • @Saurabh:你明白我的意思,但计算出来你不能在那里做你想做的事情。现在你可以通过压缩内存中的图像来伪造,但这会很昂贵。我想解决方法是改用本机 SDK。
  • 是的,这里的大多数人都建议我使用 NDK 路径,但我在 Linux 环境中也没有 C 中的 exp。但是调整大小不是一个选项,因为客户端对图像质量过于具体,如果你能提供任何关于在 NDK 中做这件事的指针,那将是很棒的
  • 好吧,您可以做的另一件事是将图像切碎并将它们分页到设备的闪存卡中。它会更复杂,但你可能能够达到相同的效果。此外,如果您滚动自己的位图例程,您可以流出文件(但无法渲染)。然而,所有这些事情都很讨厌。无论如何,祝你好运!
  • 感谢 Mikola 早先已经处理了建议的问题,而不是位图 我必须显示 PDF 我尝试使用所有技术尽我所能,但这种方法真的很混乱,也太棘手和错误:(跨度>
【解决方案4】:

您是想显示这张超大图片还是只是想保存它?

  1. 如果您尝试显示它。将图像切割成瓷砖。然后只显示正在查看的图块。如果用户缩小,您需要在显示整个内容之前减小位图的大小。

  2. 如果您尝试保存它,请尝试通过剪切图像将其分段保存到同一文件中。

在内存中加载 2 个 1m 的文件,然后创建一个 2m 的文件,只为您的图像留下 4M 的内存。动态加载和卸载内存解决了这个问题,类似于谷歌地图上的瓦片或其他面向地图的解决方案中的动态缩放。

【讨论】:

    【解决方案5】:

    您可能会从here 那里得到一些想法。

    【讨论】:

      【解决方案6】:

      无需将图像加载到内存中,您可以使用 inJustDecodeBounds 获取图像的大小。 Bitmap 返回 null,但所有参数均已设置。您可以相应地缩小图像。

      如果您的 JPEG 图像每个为 1 MiB,则转换为 BMP 确实会占用大量内存。您可以通过图像的尺寸轻松计算其 BMP 等效值。如此大的图像的转换预计确实会崩溃。 Android 将其应用程序限制为仅 16 MiB VM。

      也使用 RGB_565 而不是 ARGB_8888。

      所以您唯一的解决方案是: (a) 使用 BitmapFactory.Options.inSampleSize 缩小图像 要么 (b) 在没有 16 MiB 限制的情况下使用 Android NDK。

      【讨论】:

      • hmmm 不错的想法,但是如果我不将位图加载到内存中并且我害怕使用 NDK,我将如何绘制它:P 好吧,我正在等待一个好的响应
      • 使用 inJustDecodeBounds=true 可以在不打开图像的情况下找到图像的尺寸。相应地设置 inSampleSize 参数。然后设置 inJustDecodeBounds=false。然后最后打开图片。
      • 您好,我尝试了您的建议并更新了问题,您能建议我如何使用 ndk 加入吗
      • 不,抱歉,我对 Android 不太熟悉。我不知道如何使用 NDK。
      • 虽然它不能解决我的问题,但你的回答会帮助解决很多问题,所以我接受你的回答谢谢
      【解决方案7】:

      位图的内存表示占用的空间不必与文件大小密切对应。所以即使你有 3mb 内存可供 jvm 使用,你仍然可能会得到 OutOfMemoryException。

      您的代码正在同时创建三个内存中的图像。如果您可以在不阅读完整文件的情况下找到两个图像的大小,则可以修改代码以使内存中一次只有一个源图像。如果这还不够,您可能需要某种流式传输方法来读取图像。

      【讨论】:

      • 就像没有加载到内存中你不会得到图像的大小所以我必须这样做我能做的就是在 darwing 第一个位图删除它之后,但这似乎没有太大差异跨度>
      • 查看 Sumon 的答案以获取图像大小而不读取整个图像
      • 另一个技巧可能是只绘制用户一次可以看到的图像部分(考虑到屏幕尺寸)。然后处理滚动事件以按需加载更多内容,同时释放已滚动屏幕部分的内存。
      • 之前我必须实现 pdf 而不是图像,这种方法往往会导致我采用与 pdf 渲染相同的忙碌方法我已经尝到了这样做的痛苦,但是我找到了一个适用的解决方案我和我用更重的尺寸测试它,看看它是否适合我
      猜你喜欢
      • 1970-01-01
      • 2016-08-28
      • 2011-10-21
      • 1970-01-01
      • 1970-01-01
      • 2023-04-10
      • 2014-12-02
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多