Android - Reduce image file size
Asked Answered
C

3

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

Creamcolored answered 16/6, 2012 at 6:11 Comment(1)
whether the source image png or jpg?Hurtless
L
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.

Lowndes answered 16/6, 2012 at 6:18 Comment(1)
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?Creamcolored
H
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

Hilda answered 4/11, 2016 at 9:42 Comment(11)
Obviously this ISN'T complete. The variable "photo" isn't even declared locally. Also, it uses recursion, NOT a good idea.Lamanna
@Lamanna please feel free to edit and add the missing things,Hilda
@Pawan Y is Recursion is not a good Idea, entire android onDraw onLayout onMeasure uses recursion to deliver viewHilda
@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.Brassie
@Brassie please feel free to share your findings, and to add the missing variables to improve the answerHilda
@Brassie are you having any trouble sharing i can help you outHilda
@Brassie did you really try what you said or just posted it without substantial proofHilda
@war_Hero: I did bro, & my code is live now. Thanks bro :-) TCBrassie
@Brassie can you share your working code with us so future visitors can get a look at how its done?Stockholder
what is variable "photo" ?Skiffle
it should be file I believe instead of photo, but it's the input @RaviVaniyaHilda
B
-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

Biskra answered 20/8, 2019 at 19:12 Comment(1)
how does your answer improve the previously accepted answer?Salim

© 2022 - 2024 — McMap. All rights reserved.