alasarr
alasarr

Reputation: 1605

BitmapFactory.decodeFile out of memory with images 2400x2400

I need to send an image from a file to a server. The server request the image in a resolution of 2400x2400.

What I'm trying to do is:

1) Get a Bitmap using BitmapFactory.decodeFile using the correct inSampleSize.

2) Compress the image in JPEG with a quality of 40%

3) Encode the image in base64

4) Sent to the server

I cannot achieve the first step, it throws an out of memory exception. I'm sure the inSampleSize is correct but I suppose even with inSampleSize the Bitmap is huge (around 30 MB in DDMS).

Any ideas how can do it? Can I do these steps without created a bitmap object? I mean doing it on filesystem instead of RAM memory.

This is the current code:

// The following function calculate the correct inSampleSize
Bitmap image = Util.decodeSampledBitmapFromFile(imagePath, width,height);   
// compressing the image
ByteArrayOutputStream baos = new ByteArrayOutputStream();
image.compress(Bitmap.CompressFormat.JPEG, 40, baos);
// encode image
String encodedImage = Base64.encodeToString(baos.toByteArray(),Base64.DEFAULT));


public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
    // Raw height and width of image
    final int height = options.outHeight;
    final int width = options.outWidth;
    int inSampleSize = 1;

    if (height > reqHeight || width > reqWidth) {

        // Calculate ratios of height and width to requested height and width
        final int heightRatio = Math.round((float) height / (float) reqHeight);
        final int widthRatio = Math.round((float) width / (float) reqWidth);

        // Choose the smallest ratio as inSampleSize value, this will guarantee
        // a final image with both dimensions larger than or equal to the
        // requested height and width.
        inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
    }

    return inSampleSize;
}

public static Bitmap decodeSampledBitmapFromFile(String path,int reqWidth, int reqHeight) {

    // First decode with inJustDecodeBounds=true to check dimensions
    final BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeFile(path, options);

    // Calculate inSampleSize
    options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

    // Decode bitmap with inSampleSize set
    options.inJustDecodeBounds = false;
    return BitmapFactory.decodeFile(path,options);
}

Upvotes: 16

Views: 24451

Answers (3)

you can skip ARGB_8888, and use RGB_565 instead, and then dither then images to preserve good quality

 BitmapFactory.Options options = new BitmapFactory.Options();
 options.inJustDecodeBounds = false;
 options.inPreferredConfig = Config.RGB_565;
 options.inDither = true;

Upvotes: 52

IAmGroot
IAmGroot

Reputation: 13855

Do NOT load the image as a bitmap, convert it to an array, then send it.

instead:

Read it as a file, in JPG format. Use the files byte array, to encode it, and send the file across.

Loading it to bitmap, is going to cause huge memory issues unnecessarily. An image, reperesented in Bitmap format, will take ~20x or more memory than neccessary.

On the server side, you will need to treat it as a file too. rather than a bitmap.

Here is a link to loading a file to byte[] : Elegant way to read file into byte[] array in Java

Upvotes: 1

Blackbelt
Blackbelt

Reputation: 157457

you have to use BitmapFactory.Options with inJustDecodeBounds set to true. This way you can load information about the bitmap and calculate the value for downsampling it (inSampleSize for instance)

Upvotes: 1

Related Questions