Android: Resize a large bitmap file to scaled output file

I have a large bitmap (say 3888x2592) in a file. Now, I want to resize that bitmap to 800x533 and save it to another file. I normally would scale the bitmap by calling Bitmap.createBitmap method but it needs a source bitmap as the first argument, which I can't provide because loading the original image into a Bitmap object would of course exceed the memory (see here, for example).

I also can't read the bitmap with, for example, BitmapFactory.decodeFile(file, options) , providing a BitmapFactory.Options.inSampleSize , because I want to resize it to an exact width and height. Using inSampleSize would resize the bitmap to 972x648 (if I use inSampleSize=4 ) or to 778x518 (if I use inSampleSize=5 , which isn't even a power of 2).

I would also like to avoid reading the image using inSampleSize with, for example, 972x648 in a first step and then resizing it to exactly 800x533 in a second step, because the quality would be poor compared to a direct resizing of the original image.

To sum up my question: Is there a way to read a large image file with 10MP or more and save it to a new image file, resized to a specific new width and height, without getting an OutOfMemory exception?

I also tried BitmapFactory.decodeFile(file, options) and setting the Options.outHeight and Options.outWidth values manually to 800 and 533, but it doesn't work that way.


No. I'd love for someone to correct me, but I accepted the load/resize approach you tried as a compromise.

Here are the steps for anyone browsing:

  • Calculate the maximum possible inSampleSize that still yields an image larger than your target.
  • Load the image using BitmapFactory.decodeFile(file, options) , passing inSampleSize as an option.
  • Resize to the desired dimensions using Bitmap.createScaledBitmap() .

  • 贾斯汀回答翻译成代码(完美的作品):

    private Bitmap getBitmap(String path) {
    
    Uri uri = getImageUri(path);
    InputStream in = null;
    try {
        final int IMAGE_MAX_SIZE = 1200000; // 1.2MP
        in = mContentResolver.openInputStream(uri);
    
        // Decode image size
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeStream(in, null, options);
        in.close();
    
    
    
        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 resultBitmap = null;
        in = mContentResolver.openInputStream(uri);
        if (scale > 1) {
            scale--;
            // scale to max possible inSampleSize that still yields an image
            // larger than target
            options = new BitmapFactory.Options();
            options.inSampleSize = scale;
            resultBitmap = BitmapFactory.decodeStream(in, null, options);
    
            // resize to desired dimensions
            int height = resultBitmap.getHeight();
            int width = resultBitmap.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(resultBitmap, (int) x, 
               (int) y, true);
            resultBitmap.recycle();
            resultBitmap = scaledBitmap;
    
            System.gc();
        } else {
            resultBitmap = BitmapFactory.decodeStream(in);
        }
        in.close();
    
        Log.d(TAG, "bitmap size - width: " +resultBitmap.getWidth() + ", height: " + 
           resultBitmap.getHeight());
        return resultBitmap;
    } catch (IOException e) {
        Log.e(TAG, e.getMessage(),e);
        return null;
    }
    

    This is 'Mojo Risin's and 'Ofir's solutions "combined". This will give you a proportionally resized image with the boundaries of max width and max height.

  • It only reads meta data to get the original size (options.inJustDecodeBounds)
  • It uses a rought resize to save memory (itmap.createScaledBitmap)
  • It uses a precisely resized image based on the rough Bitamp created earlier.
  • For me it has been performing fine on 5 MegaPixel images an below.

    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);
    }
    
    链接地址: http://www.djcxy.com/p/93072.html

    上一篇: 如何将文件从'资产'文件夹复制到SD卡?

    下一篇: Android:将大型位图文件调整为缩放输出文件