Reduce the size of a bitmap to a specified size in Android
Asked Answered
A

3

38

I want to reduce the size of a bitmap to 200kb exactly. I get an image from the sdcard, compress it and save it to the sdcard again with a different name into a different directory. Compression works fine (3 mb like image is compressed to around 100 kb). I wrote the following lines of code for this:

String imagefile ="/sdcard/DCIM/100ANDRO/DSC_0530.jpg";
Bitmap bm = ShrinkBitmap(imagefile, 300, 300);

//this method compresses the image and saves into a location in sdcard
    Bitmap ShrinkBitmap(String file, int width, int height){
           
         BitmapFactory.Options bmpFactoryOptions = new BitmapFactory.Options();
            bmpFactoryOptions.inJustDecodeBounds = true;
            Bitmap bitmap = BitmapFactory.decodeFile(file, bmpFactoryOptions);
             
            int heightRatio = (int)Math.ceil(bmpFactoryOptions.outHeight/(float)height);
            int widthRatio = (int)Math.ceil(bmpFactoryOptions.outWidth/(float)width);
             
            if (heightRatio > 1 || widthRatio > 1)
            {
             if (heightRatio > widthRatio)
             {
              bmpFactoryOptions.inSampleSize = heightRatio;
             } else {
              bmpFactoryOptions.inSampleSize = widthRatio; 
             }
            }
             
            bmpFactoryOptions.inJustDecodeBounds = false;
            bitmap = BitmapFactory.decodeFile(file, bmpFactoryOptions);
            
            ByteArrayOutputStream stream = new ByteArrayOutputStream();   
            bitmap.compress(Bitmap.CompressFormat.JPEG, 100, stream);   
            byte[] imageInByte = stream.toByteArray(); 
            //this gives the size of the compressed image in kb
            long lengthbmp = imageInByte.length / 1024; 
            
            try {
                bitmap.compress(CompressFormat.JPEG, 100, new FileOutputStream("/sdcard/mediaAppPhotos/compressed_new.jpg"));
            } catch (FileNotFoundException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            
            
         return bitmap;
        }
    
Apodosis answered 6/6, 2013 at 5:13 Comment(8)
change width and height of that image..Sawfly
you mean to make the image 200x200?Apodosis
yeah, as per you want 200kb.. try until you get your result..Sawfly
ok, if I give a constant width and a height, will the images be of the same size always?, I have already done that: ShrinkBitmap(imagefile, 300, 300)Apodosis
no, change width and height according to your size.. if you get 100kb on 300*300 then for making size 200kb, raise both width and height..Sawfly
200 x 200 = 81kb 300 x 300 = 81kb 350 x 350 = 81kb 400 x 300 = 310kb 400 x 400 = 310kb Above are the values that I get, doesn't seem to be hitting 200kbApodosis
let us continue this discussion in chatSawfly
@Kalpesh - see the answer, I answered it.Apodosis
A
82

I found an answer that works perfectly for me:

/**
 * reduces the size of the image
 * @param image
 * @param maxSize
 * @return
 */
public Bitmap getResizedBitmap(Bitmap image, int maxSize) {
    int width = image.getWidth();
    int height = image.getHeight();

    float bitmapRatio = (float)width / (float) height;
    if (bitmapRatio > 1) {
        width = maxSize;
        height = (int) (width / bitmapRatio);
    } else {
        height = maxSize;
        width = (int) (height * bitmapRatio);
    }
    return Bitmap.createScaledBitmap(image, width, height, true);
}

calling the method:

Bitmap converetdImage = getResizedBitmap(photo, 500);

Where photo is your bitmap

Apodosis answered 5/8, 2014 at 10:27 Comment(7)
This function will reduce the bitmap size if its larger, but what about the smaller bitmaps. Will they be enlarged as per the specification?Brierroot
Why it gives be black bitmap?Roley
@Apodosis what is maxSize? in your method calling is 500, 500 kb or 500 mb?Jacquijacquie
The Max is referring to the Width and height, not the size. developer.android.com/reference/android/graphics/…, int, int, boolean)Bullfighter
@Apodosis 500 mb maybe not but this could be 500kb 500KB 500ko ...Kingwood
This doesn't make any sense at all. Is maxSize size of image in pixels or file size in Kilobytes as you wanted? I'm also looking for a similar solution but this is the closest I've come and yet I can't seem to understand what that method is doing in relation to the problem.Underhung
https://mcmap.net/q/410485/-resizing-a-bitmap-to-a-fixed-value-but-without-changing-the-aspect-ratio This has better implementationAbney
S
11

Here is the solution that limits image quality without changing width and height. The main idea of this approach is to compress bitmap inside the loop while output size is bigger than maxSizeBytesCount. Credits to this question for the answer. This code block shows only the logic of reducing image quality:

        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        int currSize;
        int currQuality = 100;

        do {
            bitmap.compress(Bitmap.CompressFormat.JPEG, currQuality, stream);
            currSize = stream.toByteArray().length;
            // limit quality by 5 percent every time
            currQuality -= 5;

        } while (currSize >= maxSizeBytes);

Here is the full method:

public class ImageUtils {

    public byte[] compressBitmap(
            String file, 
            int width, 
            int height,
            int maxSizeBytes
    ) {
        BitmapFactory.Options bmpFactoryOptions = new BitmapFactory.Options();
        bmpFactoryOptions.inJustDecodeBounds = true;
        Bitmap bitmap;

        int heightRatio = (int)Math.ceil(bmpFactoryOptions.outHeight/(float)height);
        int widthRatio = (int)Math.ceil(bmpFactoryOptions.outWidth/(float)width);

        if (heightRatio > 1 || widthRatio > 1)
        {
            if (heightRatio > widthRatio)
            {
                bmpFactoryOptions.inSampleSize = heightRatio;
            } else {
                bmpFactoryOptions.inSampleSize = widthRatio;
            }
        }

        bmpFactoryOptions.inJustDecodeBounds = false;
        bitmap = BitmapFactory.decodeFile(file, bmpFactoryOptions);

        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        int currSize;
        int currQuality = 100;

        do {
            bitmap.compress(Bitmap.CompressFormat.JPEG, currQuality, stream);
            currSize = stream.toByteArray().length;
            // limit quality by 5 percent every time
            currQuality -= 5;

        } while (currSize >= maxSizeBytes);

        return stream.toByteArray();
    }
}
Suspicion answered 2/6, 2019 at 7:5 Comment(4)
Not so good approach, compression is demanding operation. It would be nice if we could predict quality and compress bitmap only once. Still, creative thinking.Ative
I wish that the solution you describe exists, but I think it is impossible with JPEG :(Suspicion
"Attempt to invoke virtual method 'boolean android.graphics.Bitmap.compress(android.graphics.Bitmap$CompressFormat, int, java.io.OutputStream)' on a null object reference" in bitmap.compress(Bitmap.CompressFormat.JPEG, currQuality, stream);Starveling
the previous solution enter infinitive loop just take " ByteArrayOutputStream stream = new ByteArrayOutputStream();" inside the loopRationalize
C
-1

The method by @TharakaNirmana works fine, maxSize is in Kylobytes

Example:

fun getResizedBitmap(image: Bitmap, maxSize: Int): Bitmap? {
    var width = image.width
    var height = image.height
    val bitmapRatio = width.toFloat() / height.toFloat()
    if (bitmapRatio > 1) {
        width = maxSize
        height = (width / bitmapRatio).toInt()
    } else {
        height = maxSize
        width = (height * bitmapRatio).toInt()
    }
    return Bitmap.createScaledBitmap(image, width, height, true)
}

Usage:

bitmap.let {
    val data = it.value

    if (data != null) {

        var image = viewModel.getResizedBitmap(data, 170)
        Log.d ("bitmap size", image!!.byteCount.toString()) ...

Answer

2021-12-18 12:38:11.218 25502-25502/com.jdsalasc.sophosSolutions D/bitmap size: 67200 2021-12-18 12:39:11.673 25745-25745/com.jdsalasc.sophosSolutions D/bitmap size: 67200 2021-12-18 12:39:14.511 25745-25745/com.jdsalasc.sophosSolutions D/bitmap size: 90000 2021-12-18 12:39:20.423 25745-25745/com.jdsalasc.sophosSolutions D/bitmap size: 67200 2021-12-18 12:39:40.112 25907-25907/com.jdsalasc.sophosSolutions D/bitmap size: 120000 2021-12-18 12:39:43.322 25907-25907/com.jdsalasc.sophosSolutions D/bitmap size: 120000 2021-12-18 12:39:47.314 25907-25907/com.jdsalasc.sophosSolutions D/bitmap size: 160000 2021-12-18 12:40:17.231 26069-26069/com.jdsalasc.sophosSolutions D/bitmap size: 115600 2021-12-18 12:40:19.583 26069-26069/com.jdsalasc.sophosSolutions D/bitmap size: 86360

how you see, the bytes size never comes over 150 kb

Thank you Zoe and TharakaNirmana c:

Churchman answered 18/12, 2021 at 17:50 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.