60

I have an URI image file, and I want to reduce its size to upload it. Initial image file size depends from mobile to mobile (can be 2MB as can be 500KB), but I want final size to be about 200KB, so that I can upload it.
From what I read, I have (at least) 2 choices:

  • Using BitmapFactory.Options.inSampleSize, to subsample original image and get a smaller image;
  • Using Bitmap.compress to compress the image specifying compression quality.

What's the best choice?


I was thinking to initially resize image width/height until width or height is above 1000px (something like 1024x768 or others), then compress image with decreasing quality until file size is above 200KB. Here's an example:

int MAX_IMAGE_SIZE = 200 * 1024; // max final file size
Bitmap bmpPic = BitmapFactory.decodeFile(fileUri.getPath());
if ((bmpPic.getWidth() >= 1024) && (bmpPic.getHeight() >= 1024)) {
    BitmapFactory.Options bmpOptions = new BitmapFactory.Options();
    bmpOptions.inSampleSize = 1;
    while ((bmpPic.getWidth() >= 1024) && (bmpPic.getHeight() >= 1024)) {
        bmpOptions.inSampleSize++;
        bmpPic = BitmapFactory.decodeFile(fileUri.getPath(), bmpOptions);
    }
    Log.d(TAG, "Resize: " + bmpOptions.inSampleSize);
}
int compressQuality = 104; // quality decreasing by 5 every loop. (start from 99)
int streamLength = MAX_IMAGE_SIZE;
while (streamLength >= MAX_IMAGE_SIZE) {
    ByteArrayOutputStream bmpStream = new ByteArrayOutputStream();
    compressQuality -= 5;
    Log.d(TAG, "Quality: " + compressQuality);
    bmpPic.compress(Bitmap.CompressFormat.JPEG, compressQuality, bmpStream);
    byte[] bmpPicByteArray = bmpStream.toByteArray();
    streamLength = bmpPicByteArray.length;
    Log.d(TAG, "Size: " + streamLength);
}
try {
    FileOutputStream bmpFile = new FileOutputStream(finalPath);
    bmpPic.compress(Bitmap.CompressFormat.JPEG, compressQuality, bmpFile);
    bmpFile.flush();
    bmpFile.close();
} catch (Exception e) {
    Log.e(TAG, "Error on saving file");
}

Is there a better way to do it? Should I try to keep using all 2 methods or only one? Thanks

GAMA
  • 5,958
  • 14
  • 79
  • 126
KitKat
  • 715
  • 2
  • 7
  • 10

3 Answers3

31

Using Bitmap.compress() you just specify compression algorithm and by the way compression operation takes rather big amount of time. If you need to play with sizes for reducing memory allocation for your image, you exactly need to use scaling of your image using Bitmap.Options, computing bitmap bounds at first and then decoding it to your specified size.

The best sample that I found on StackOverflow is this one.

Community
  • 1
  • 1
teoREtik
  • 7,886
  • 15
  • 46
  • 65
  • 3
    My first idea was to iterate scaling of image (using `inSampleSize`) until image file get lower than `MAX_IMAGE_SIZE` but, since I have to save my bitmap to a file, I have to use `Bitmap.compress()` (or maybe are there different ways to save it on a file?), and calling it giving 100% quality, image get bigger than `MAX_IMAGE_SIZE`, so I should anyway iterate `Bitmap.compress()` (decreasing quality) until file size < `MAX_IMAGE_SIZE`. Maybe are there better solutions? – KitKat Jun 18 '12 at 11:47
1

Most of the answers i found were just pieces that i had to put together to get a working code, which is posted below

 public void compressBitmap(File file, int sampleSize, int quality) {
        try {
           BitmapFactory.Options options = new BitmapFactory.Options();
            options.inSampleSize = sampleSize;
            FileInputStream inputStream = new FileInputStream(file);

            Bitmap selectedBitmap = BitmapFactory.decodeStream(inputStream, null, options);
            inputStream.close();

            FileOutputStream outputStream = new FileOutputStream("location to save");
            selectedBitmap.compress(Bitmap.CompressFormat.JPEG, quality, outputStream);
            outputStream.close();
            long lengthInKb = photo.length() / 1024; //in kb
            if (lengthInKb > SIZE_LIMIT) {
               compressBitmap(file, (sampleSize*2), (quality/4));
            }

            selectedBitmap.recycle();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

2 parameters sampleSize and quality plays an important role

sampleSize is used to subsample the original image and return a smaller image, ie
SampleSize == 4 returns an image that is 1/4 the width/height of the original.

quality is used to hint the compressor, input range is between 0-100. 0 meaning compress for small size, 100 meaning compress for max quality

Aniruddha K.M
  • 7,361
  • 3
  • 43
  • 52
-1

BitmapFactory.Options - Reduces Image Size (In Memory)

Bitmap.compress() - Reduces Image Size (In Disk)


Refer to this link for more information about using both of them: https://android.jlelse.eu/loading-large-bitmaps-efficiently-in-android-66826cd4ad53

Ziad H.
  • 528
  • 1
  • 5
  • 20