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,874
  • 13
  • 76
  • 124
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,824
  • 14
  • 45
  • 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

Mightian
  • 7,149
  • 3
  • 39
  • 50
  • Obviously this ISN'T complete. The variable "photo" isn't even declared locally. Also, it uses recursion, NOT a good idea. – Shroud Jan 24 '17 at 23:21
  • @Shroud please feel free to edit and add the missing things, – Mightian Jan 25 '17 at 03:36
  • @Pawan Y is Recursion is not a good Idea, entire android onDraw onLayout onMeasure uses recursion to deliver view – Mightian Mar 22 '17 at 10:29
  • @war_Hero : Bro, using Logs i printed the start & end time. It was taking more time than the normal logic which i wrote without recursion. Secondly, the code which you wrote here has lots of undefined variables, I would be happy, if you could give some comments for those undefined variables. – Pawan Mar 22 '17 at 10:33
  • @Pawan please feel free to share your findings, and to add the missing variables to improve the answer – Mightian Mar 22 '17 at 10:38
  • @Pawan are you having any trouble sharing i can help you out – Mightian Mar 22 '17 at 10:47
  • @Pawan did you really try what you said or just posted it without substantial proof – Mightian Mar 23 '17 at 10:08
  • @war_Hero: I did bro, & my code is live now. Thanks bro :-) TC – Pawan Mar 23 '17 at 10:24
  • 2
    @Pawan can you share your working code with us so future visitors can get a look at how its done? – kilokahn Jan 20 '19 at 18:08
  • what is variable "photo" ? – Ravi Vaniya Nov 27 '19 at 09:27
  • it should be file I believe instead of photo, but it's the input @RaviVaniya – Mightian Nov 27 '19 at 09:29
0

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.
  • 376
  • 1
  • 4
  • 12