Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

BitmapFactory.decodeFile out of memory with images 2400x2400

Tags:

android

bitmap

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);
}
like image 960
alasarr Avatar asked Oct 30 '13 09:10

alasarr


3 Answers

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;
like image 126
Björn Hallström Avatar answered Oct 19 '22 14:10

Björn Hallström


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)

like image 34
Blackbelt Avatar answered Oct 19 '22 14:10

Blackbelt


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

like image 33
IAmGroot Avatar answered Oct 19 '22 14:10

IAmGroot