我在一个文件中有一个大的位图(比如3888x2592)。现在,我想将位图大小调整为800x533,并将其保存到另一个文件中。 我通常通过调用bitmap来缩放位图。createBitmap方法,但它需要一个源位图作为第一个参数,我不能提供,因为加载原始图像到位图对象当然会超过内存(例如,见这里)。

我也不能读取位图,例如,BitmapFactory.decodeFile(文件,选项),提供BitmapFactory.Options。inSampleSize,因为我想把它调整到精确的宽度和高度。使用inSampleSize将位图大小调整为972x648(如果我使用inSampleSize=4)或778x518(如果我使用inSampleSize=5,这甚至不是2的幂)。

我还希望避免在第一步中使用inSampleSize读取图像,例如,在第一步中使用972x648,然后在第二步中将其大小调整为800x533,因为与直接调整原始图像的大小相比,质量会较差。

总结一下我的问题: 是否有一种方法来读取一个10MP或更多的大图像文件,并将其保存到一个新的图像文件,调整到一个特定的新宽度和高度,而不得到OutOfMemory异常?

我还尝试了BitmapFactory.decodeFile(文件,选项)和设置选项。out theight and Options。outidth值手动为800和533,但它不是这样工作的。


当前回答

如果你想一步调整大小你可以加载整个位图如果 android:largeHeap = true,但正如你所看到的,这是不可取的。

From docs: android:largeHeap Whether your application's processes should be created with a large Dalvik heap. This applies to all processes created for the application. It only applies to the first application loaded into a process; if you're using a shared user ID to allow multiple applications to use a process, they all must use this option consistently or they will have unpredictable results. Most apps should not need this and should instead focus on reducing their overall memory usage for improved performance. Enabling this also does not guarantee a fixed increase in available memory, because some devices are constrained by their total available memory.

其他回答

为什么不使用API呢?

int h = 48; // height in pixels
int w = 48; // width in pixels    
Bitmap scaled = Bitmap.createScaledBitmap(largeBitmap, w, h, true);

为了以“正确”的方式缩放图像,而不跳过任何像素,您必须连接到图像解码器来逐行执行下采样。Android(以及基于它的Skia库)没有提供这样的钩子,所以你必须自己滚动。假设您谈论的是jpeg图像,最好的办法是直接使用C语言中的libjpeg。

考虑到其中的复杂性,对于图像预览类型的应用程序来说,使用两步先采样再缩放的方法可能是最好的。

上面的代码更简洁一些。InputStreams最终关闭了包装,以确保它们也被关闭:

*请注意 输入:InputStream is, int w, int h 输出:位图

    try
    {

        final int inWidth;
        final int inHeight;

        final File tempFile = new File(temp, System.currentTimeMillis() + is.toString() + ".temp");

        {

            final FileOutputStream tempOut = new FileOutputStream(tempFile);

            StreamUtil.copyTo(is, tempOut);

            tempOut.close();

        }



        {

            final InputStream in = new FileInputStream(tempFile);
            final BitmapFactory.Options options = new BitmapFactory.Options();

            try {

                // decode image size (decode metadata only, not the whole image)
                options.inJustDecodeBounds = true;
                BitmapFactory.decodeStream(in, null, options);

            }
            finally {
                in.close();
            }

            // save width and height
            inWidth = options.outWidth;
            inHeight = options.outHeight;

        }

        final Bitmap roughBitmap;

        {

            // decode full image pre-resized
            final InputStream in = new FileInputStream(tempFile);

            try {

                final BitmapFactory.Options options = new BitmapFactory.Options();
                // calc rought re-size (this is no exact resize)
                options.inSampleSize = Math.max(inWidth/w, inHeight/h);
                // decode full image
                roughBitmap = BitmapFactory.decodeStream(in, null, options);

            }
            finally {
                in.close();
            }

            tempFile.delete();

        }

        float[] values = new float[9];

        {

            // calc exact destination size
            Matrix m = new Matrix();
            RectF inRect = new RectF(0, 0, roughBitmap.getWidth(), roughBitmap.getHeight());
            RectF outRect = new RectF(0, 0, w, h);
            m.setRectToRect(inRect, outRect, Matrix.ScaleToFit.CENTER);
            m.getValues(values);

        }

        // resize bitmap
        final Bitmap resizedBitmap = Bitmap.createScaledBitmap(roughBitmap, (int) (roughBitmap.getWidth() * values[0]), (int) (roughBitmap.getHeight() * values[4]), true);

        return resizedBitmap;

    }
    catch (IOException e) {

        logger.error("Error:" , e);
        throw new ResourceException("could not create bitmap");

    }

在Android开发者网站上有一篇关于这个问题的很棒的文章: 有效加载大位图

考虑到你想要调整大小到精确的大小,并想要保持尽可能多的质量,我认为你应该试试这个。

通过调用BitmapFactory.decodeFile并提供checkSizeOptions.inJustDecodeBounds来查找调整后图像的大小 计算您可以在设备上使用的不超过内存的inSampleSize的最大值。bitmapSizeInBytes = 2*width*height;一般来说,对于你的图片inSampleSize=2就可以了,因为你只需要2*1944x1296)=4.8Mbб,这应该是在内存中 使用BitmapFactory.decodeFile和inSampleSize来加载位图 将位图缩放到精确的大小。

动机:多步缩放可以提供更高质量的图片,但不能保证它会比使用high inSampleSize更好。 实际上,我认为你也可以使用inSampleSize 5(不是pow of 2)在一个操作中直接缩放。或者用4,然后你可以在UI中使用那个图像。如果你把它发送到服务器,那么你可以在服务器端进行精确的缩放,这允许你使用高级缩放技术。

注意:如果在步骤3中加载的位图至少是4倍大(所以4*targetWidth < width),你可能会使用几次调整来获得更好的质量。 至少这在通用java中是有效的,在android中你没有指定用于缩放的插值的选项 http://today.java.net/pub/a/today/2007/04/03/perils-of-image-getscaledinstance.html