我在一个文件中有一个大的位图(比如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,但它不是这样工作的。


当前回答

当我有大位图,我想解码他们调整大小我使用以下

BitmapFactory.Options options = new BitmapFactory.Options();
InputStream is = null;
is = new FileInputStream(path_to_file);
BitmapFactory.decodeStream(is,null,options);
is.close();
is = new FileInputStream(path_to_file);
// here w and h are the desired width and height
options.inSampleSize = Math.max(options.outWidth/w, options.outHeight/h);
// bitmap is the resized bitmap
Bitmap bitmap = BitmapFactory.decodeStream(is,null,options);

其他回答

如果你想一步调整大小你可以加载整个位图如果 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.

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

通过调用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

不。我希望有人能纠正我,但我接受了你作为妥协尝试的加载/调整大小的方法。

下面是浏览网页的步骤:

计算最大可能的在samplesize仍然产生比你的目标更大的图像。 使用BitmapFactory.decodeFile(file, options)加载图像,传入samplesize作为选项。 使用Bitmap.createScaledBitmap()将大小调整到所需的尺寸。

这对我很管用。该函数获取sd卡上文件的路径,并返回可显示的最大大小的位图。 代码是从Ofir与一些变化,如图像文件在sd代替资源和witdth和高度是从显示对象。

private Bitmap makeBitmap(String path) {

    try {
        final int IMAGE_MAX_SIZE = 1200000; // 1.2MP
        //resource = getResources();

        // Decode image size
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeFile(path, options);

        int scale = 1;
        while ((options.outWidth * options.outHeight) * (1 / Math.pow(scale, 2)) >
                IMAGE_MAX_SIZE) {
            scale++;
        }
        Log.d("TAG", "scale = " + scale + ", orig-width: " + options.outWidth + ", orig-height: " + options.outHeight);

        Bitmap pic = null;
        if (scale > 1) {
            scale--;
            // scale to max possible inSampleSize that still yields an image
            // larger than target
            options = new BitmapFactory.Options();
            options.inSampleSize = scale;
            pic = BitmapFactory.decodeFile(path, options);

            // resize to desired dimensions

            Display display = getWindowManager().getDefaultDisplay();
            Point size = new Point();
            display.getSize(size);
            int width = size.y;
            int height = size.x;

            //int height = imageView.getHeight();
            //int width = imageView.getWidth();
            Log.d("TAG", "1th scale operation dimenions - width: " + width + ", height: " + height);

            double y = Math.sqrt(IMAGE_MAX_SIZE
                    / (((double) width) / height));
            double x = (y / height) * width;

            Bitmap scaledBitmap = Bitmap.createScaledBitmap(pic, (int) x, (int) y, true);
            pic.recycle();
            pic = scaledBitmap;

            System.gc();
        } else {
            pic = BitmapFactory.decodeFile(path);
        }

        Log.d("TAG", "bitmap size - width: " +pic.getWidth() + ", height: " + pic.getHeight());
        return pic;

    } catch (Exception e) {
        Log.e("TAG", e.getMessage(),e);
        return null;
    }

}

这是“Mojo Risin”和“Ofir”解决方案的“结合”。这将给你一个按比例调整图像的最大宽度和最大高度的边界。

它只读取元数据以获得原始大小(选项。 它使用粗略的调整大小来节省内存(itmap.createScaledBitmap) 它使用了基于之前创建的粗糙Bitamp精确调整大小的图像。

对我来说,它在500万像素以下的图像上表现良好。

try
{
    int inWidth = 0;
    int inHeight = 0;

    InputStream in = new FileInputStream(pathOfInputImage);

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

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

    // decode full image pre-resized
    in = new FileInputStream(pathOfInputImage);
    options = new BitmapFactory.Options();
    // calc rought re-size (this is no exact resize)
    options.inSampleSize = Math.max(inWidth/dstWidth, inHeight/dstHeight);
    // decode full image
    Bitmap roughBitmap = BitmapFactory.decodeStream(in, null, options);

    // calc exact destination size
    Matrix m = new Matrix();
    RectF inRect = new RectF(0, 0, roughBitmap.getWidth(), roughBitmap.getHeight());
    RectF outRect = new RectF(0, 0, dstWidth, dstHeight);
    m.setRectToRect(inRect, outRect, Matrix.ScaleToFit.CENTER);
    float[] values = new float[9];
    m.getValues(values);

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

    // save image
    try
    {
        FileOutputStream out = new FileOutputStream(pathOfOutputImage);
        resizedBitmap.compress(Bitmap.CompressFormat.JPEG, 80, out);
    }
    catch (Exception e)
    {
        Log.e("Image", e.getMessage(), e);
    }
}
catch (IOException e)
{
    Log.e("Image", e.getMessage(), e);
}