在Android中从文件中解码位图的一部分

6
我有一个非常大的图像文件,比如9000x9000。由于堆大小的限制,我无法将位图加载到内存中。但是,我只需要显示此位图的一小部分,例如矩形宽度= 100-200和高度= 200-400(子位图的结果大小为100x200)。
我该如何从文件中检索此位图?
注意:我不想在100x200图像中失去质量。
谢谢!
6个回答

17

是否有可能有一个解决方案呢?例如,BitmapRegionDecoder 可以实现。它适用于API10及以上版本...

使用方法:

BitmapRegionDecoder.newInstance(...).decodeRegion(...)

显然那将是正确的答案。 - njzk2

4

使用RapidDecoder可以轻松完成。

我实际上生成了一个9000x9000大小的png文件,文件大小约为80MB,而200x400大小的区域成功加载。

import rapid.decoder.BitmapDecoder;

Bitmap bitmap = BitmapDecoder.from("big-image.png")
                             .region(145, 192, 145 + 200, 192 + 400)
                             .decode();
imageView.setImageBitmap(bitmap);

该功能适用于Android 2.2及以上版本。


哇,这听起来很有前途。整个解码过程都在JNI上完成吗?支持哪些文件格式?甚至还要支持WebP吗?它是否包括人脸检测? - android developer
+1非常好的库,同时图像质量保持在80%左右,并且可以在几乎所有设备上运行。 - Sagar G.

2
我认为你可以使用BitmapFactory方法,它允许你指定要解码的矩形区域。
public static Bitmap decodeStream (InputStream is, Rect outPadding, BitmapFactory.Options opts)

outPadding是一个输出参数。 - Addev
@Addev 我非常感兴趣...你不觉得在导入位图之前只取一些矩形是可能的吗? :) - Simone Casagranda
在哪些情况下会填充outPadding?当它是9-patch时吗?还有其他情况吗?即使是9-patch,它意味着什么,会填充什么? - android developer

1

Try this code:

public static Bitmap decodeSampledBitmapFromFile(String path, int reqWidth, int reqHeight) {
        final BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeFile(path, options);

        final int height = options.outHeight;
        final int width = options.outWidth;
        options.inPreferredConfig = Bitmap.Config.RGB_565;
        int inSampleSize = 1;
        if (height > reqHeight) {
            inSampleSize = Math.round((float) height / (float) reqHeight);
        }
        int expectedWidth = width / inSampleSize;
        if (expectedWidth > reqWidth) {
            inSampleSize = Math.round((float) width / (float) reqWidth);
        }
        options.inSampleSize = inSampleSize;
        options.inJustDecodeBounds = false;
        return BitmapFactory.decodeFile(path, options);
    }

0

试试这段代码:

private Bitmap decodeFile(File f) {
    Bitmap b = null;
    try {
        BitmapFactory.Options o = new BitmapFactory.Options();
        o.inJustDecodeBounds = true;
        FileInputStream fis = new FileInputStream(f);
        b=Bitmap.createBitmap(BitmapFactory.decodeStream(fis, null, o), 100, 200, 200, 400, null, null);
        fis.close();
    } catch (IOException e) {
    }
    return b;
}

我不确定,但这可能会给你一些想法


它会出现内存不足的错误,因为 BitmapFactory.decodeStream(fis, null, o) 方法只有在整个 Bitmap 被检索完毕后才会返回。 - Addev

0

我认为你做不到。即使在PC上,我也看不出你如何在不加载整个图像的情况下完成这项任务:例如PNG等大多数图像格式都将像素数据压缩,因此您至少需要解压IDAT块才能开始进行其他操作,这基本上会解码整个图像。

如果是我,我会尝试让服务器为我完成这项任务。你从哪里获取这张图片?不是从服务器吗?那么尝试发起一个WS请求,以获取所需的图像部分。如果图像不来自服务器,您仍然可以将其发送到您的服务器,以仅获取您想要的图像部分。


谢谢你的回答。我想实现一个MultiTouchImageView,它可以根据缩放和滚动位置仅加载可见部分的图像,从而允许加载大型图像而不会失去质量或内存崩溃。我该怎么做呢? - Addev
在这种情况下,我认为您试图做的事情从技术上来说对于移动设备的限制而言是不可能的:您无法在不将整个图像加载到内存中并仅显示所需部分的情况下为用户提供良好的体验。即使您可以仅加载图像的一部分,每次用户移动或缩放时都必须这样做...由于操作会很慢,用户体验也会很糟糕。唯一的方法是对图像大小设置某些上限。否则,在我看来您就无法做到这一点。 - Alexandru Cristescu
嗯,你的说法很有道理,但是安卓的相册是如何加载那些允许进行同等质量缩放的大图像的呢? - Addev
2
Android的美妙之处在于您可以查看应用程序的源代码...包括Android的图库。这是链接https://github.com/CyanogenMod/android_packages_apps_Gallery/tree/eclair/src/com/android/camera 我正在查看BaseImage.java(在图库子目录中)和Util.java类(特别是makeBitmap()方法),我认为它们正在进行缩放... options.inSampleSize = computeSampleSize(options, minSideLength, maxNumOfPixels); [...] - Alexandru Cristescu
1
这个答案已经过时或者不正确。在Android API 10级中,加载此大小的图像的子图像没有问题。 - Filip Hazubski

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接