74

I use this method to compress an image

if(bitmapObject.compress(Bitmap.CompressFormat.PNG, 100, fOut))
{
    ... 
}

But the image I get is much smaller ( in the dimension ) that was before the compress action.

My application need to send the compressed image thru the network - so i want to send as less data as I can ... but I must to keep the original size of the image.

Is there some other way to keep the original bitmap dimension with some compress ?

CopsOnRoad
  • 237,138
  • 77
  • 654
  • 440
Yanshof
  • 9,659
  • 21
  • 95
  • 195

6 Answers6

114

Are you sure it is smaller?

Bitmap original = BitmapFactory.decodeStream(getAssets().open("1024x768.jpg"));
ByteArrayOutputStream out = new ByteArrayOutputStream();
original.compress(Bitmap.CompressFormat.PNG, 100, out);
Bitmap decoded = BitmapFactory.decodeStream(new ByteArrayInputStream(out.toByteArray()));

Log.e("Original   dimensions", original.getWidth()+" "+original.getHeight());
Log.e("Compressed dimensions", decoded.getWidth()+" "+decoded.getHeight());

Gives

12-07 17:43:36.333: E/Original   dimensions(278): 1024 768
12-07 17:43:36.333: E/Compressed dimensions(278): 1024 768

Maybe you get your bitmap from a resource, in which case the bitmap dimension will depend on the phone screen density

Bitmap bitmap=((BitmapDrawable)getResources().getDrawable(R.drawable.img_1024x768)).getBitmap();
Log.e("Dimensions", bitmap.getWidth()+" "+bitmap.getHeight());

12-07 17:43:38.733: E/Dimensions(278): 768 576
Daniel Fekete
  • 4,988
  • 3
  • 23
  • 23
  • 6
    `original.compress(Bitmap.CompressFormat.PNG, 100, out);` value 100 is ignored because the format is png and maybe that's why compressed bitmap returns same dimensions as original – Apurva Nov 12 '15 at 12:55
  • 4
    there is no way i can find a logic or compression value for a 1.0 MB image to 0.6MB image. Even if the compression quality is set to 100 still 1.0 MB image gets converted to around 0.15-0.2 MB image. cant understand the flow of compression – ajinkya gaurkar May 10 '16 at 12:56
  • @ajinkyagaurkar Compression heavily relies on the content. What works well for one image might have little effect on another. – Mattwmaster58 Mar 17 '22 at 14:34
66

If you are using PNG format then it will not compress your image because PNG is a lossless format. use JPEG for compressing your image and use 0 instead of 100 in quality.

Quality Accepts 0 - 100

0 = MAX Compression (Least Quality which is suitable for Small images)

100 = Least Compression (MAX Quality which is suitable for Big images)

CopsOnRoad
  • 237,138
  • 77
  • 654
  • 440
Adeel
  • 859
  • 6
  • 9
  • 8
    PNG doesn't use the quality parameter - from the docs: "Hint to the compressor, 0-100. 0 meaning compress for small size, 100 meaning compress for max quality. Some formats, like PNG which is lossless, will ignore the quality setting.." – whyoz Apr 11 '13 at 18:02
  • 10
    I use 100 and it makes the picture really bad. are you sure that 100 is for best quality? – stanley santoso Jul 20 '15 at 06:03
  • 1
    You cannot compress if your image source is png.Thanks! – zionpi Nov 19 '15 at 08:41
  • @stanleysantoso FYI quality: Hint to the compressor, 0-100. 0 meaning compress for small size, 100 meaning compress for max quality. Some formats, like PNG which is lossless, will ignore the quality setting Source: https://developer.xamarin.com/api/member/Android.Graphics.Bitmap.Compress/p/Android.Graphics.Bitmap+CompressFormat/System.Int32/System.IO.Stream/ – Adeel Sep 18 '16 at 03:21
  • [More specific link](https://developer.android.com/reference/android/graphics/Bitmap#compress(android.graphics.Bitmap.CompressFormat,%20int,%20java.io.OutputStream)) – mr5 Jun 22 '18 at 07:09
21

I have done this way:

Get Compressed Bitmap from Singleton class:

ImageView imageView = (ImageView)findViewById(R.id.imageView);
Bitmap bitmap = ImageUtils.getInstant().getCompressedBitmap("Your_Image_Path_Here");
imageView.setImageBitmap(bitmap);

ImageUtils.java:

public class ImageUtils {

    public static ImageUtils mInstant;

    public static ImageUtils getInstant(){
        if(mInstant==null){
            mInstant = new ImageUtils();
        }
        return mInstant;
    }

    public  Bitmap getCompressedBitmap(String imagePath) {
        float maxHeight = 1920.0f;
        float maxWidth = 1080.0f;
        Bitmap scaledBitmap = null;
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        Bitmap bmp = BitmapFactory.decodeFile(imagePath, options);

        int actualHeight = options.outHeight;
        int actualWidth = options.outWidth;
        float imgRatio = (float) actualWidth / (float) actualHeight;
        float maxRatio = maxWidth / maxHeight;

        if (actualHeight > maxHeight || actualWidth > maxWidth) {
            if (imgRatio < maxRatio) {
                imgRatio = maxHeight / actualHeight;
                actualWidth = (int) (imgRatio * actualWidth);
                actualHeight = (int) maxHeight;
            } else if (imgRatio > maxRatio) {
                imgRatio = maxWidth / actualWidth;
                actualHeight = (int) (imgRatio * actualHeight);
                actualWidth = (int) maxWidth;
            } else {
                actualHeight = (int) maxHeight;
                actualWidth = (int) maxWidth;

            }
        }

        options.inSampleSize = calculateInSampleSize(options, actualWidth, actualHeight);
        options.inJustDecodeBounds = false;
        options.inDither = false;
        options.inPurgeable = true;
        options.inInputShareable = true;
        options.inTempStorage = new byte[16 * 1024];

        try {
            bmp = BitmapFactory.decodeFile(imagePath, options);
        } catch (OutOfMemoryError exception) {
            exception.printStackTrace();

        }
        try {
            scaledBitmap = Bitmap.createBitmap(actualWidth, actualHeight, Bitmap.Config.ARGB_8888);
        } catch (OutOfMemoryError exception) {
            exception.printStackTrace();
        }

        float ratioX = actualWidth / (float) options.outWidth;
        float ratioY = actualHeight / (float) options.outHeight;
        float middleX = actualWidth / 2.0f;
        float middleY = actualHeight / 2.0f;

        Matrix scaleMatrix = new Matrix();
        scaleMatrix.setScale(ratioX, ratioY, middleX, middleY);

        Canvas canvas = new Canvas(scaledBitmap);
        canvas.setMatrix(scaleMatrix);
        canvas.drawBitmap(bmp, middleX - bmp.getWidth() / 2, middleY - bmp.getHeight() / 2, new Paint(Paint.FILTER_BITMAP_FLAG));

        ExifInterface exif = null;
        try {
            exif = new ExifInterface(imagePath);
            int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, 0);
            Matrix matrix = new Matrix();
            if (orientation == 6) {
                matrix.postRotate(90);
            } else if (orientation == 3) {
                matrix.postRotate(180);
            } else if (orientation == 8) {
                matrix.postRotate(270);
            }
            scaledBitmap = Bitmap.createBitmap(scaledBitmap, 0, 0, scaledBitmap.getWidth(), scaledBitmap.getHeight(), matrix, true);
        } catch (IOException e) {
            e.printStackTrace();
        }
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        scaledBitmap.compress(Bitmap.CompressFormat.JPEG, 85, out);

        byte[] byteArray = out.toByteArray();

        Bitmap updatedBitmap = BitmapFactory.decodeByteArray(byteArray, 0, byteArray.length);

        return updatedBitmap;
    }

    private int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
        final int height = options.outHeight;
        final int width = options.outWidth;
        int inSampleSize = 1;

        if (height > reqHeight || width > reqWidth) {
            final int heightRatio = Math.round((float) height / (float) reqHeight);
            final int widthRatio = Math.round((float) width / (float) reqWidth);
            inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
        }
        final float totalPixels = width * height;
        final float totalReqPixelsCap = reqWidth * reqHeight * 2;

        while (totalPixels / (inSampleSize * inSampleSize) > totalReqPixelsCap) {
            inSampleSize++;
        }
        return inSampleSize;
    }
}

Dimensions are same after compressing Bitmap.

How did I checked ?

Bitmap beforeBitmap = BitmapFactory.decodeFile("Your_Image_Path_Here");
Log.i("Before Compress Dimension", beforeBitmap.getWidth()+"-"+beforeBitmap.getHeight());

Bitmap afterBitmap = ImageUtils.getInstant().getCompressedBitmap("Your_Image_Path_Here");
Log.i("After Compress Dimension", afterBitmap.getWidth() + "-" + afterBitmap.getHeight());

Output:

Before Compress : Dimension: 1080-1452
After Compress : Dimension: 1080-1452

Hope this will help you.

Hiren Patel
  • 52,124
  • 21
  • 173
  • 151
3

Here's a short means I used to reduce the size of Images that have a high byteCount (basically pixels)

fun resizeImage(image: Bitmap): Bitmap {

    val width = image.width
    val height = image.height

    val scaleWidth = width / 10
    val scaleHeight = height / 10

    if (image.byteCount <= 1000000)
        return image

    return Bitmap.createScaledBitmap(image, scaleWidth, scaleHeight, false)
}

This returns a scaled Bitmap that is over 10 times smaller than the Bitmap passed as a parameter. Might not be the most ideal solution but it works.

1

You can compress into webp to get lowest size. So firstly compress into webp and then into byteArray. After conversion into byteArray you can convert into bitmap as given below

public Bitmap compress(Bitmap yourBitmap){
    //converted into webp into lowest quality
    ByteArrayOutputStream stream = new ByteArrayOutputStream();
    yourBitmap.compress(Bitmap.CompressFormat.WEBP,0,stream);//0=lowest, 100=highest quality
    byte[] byteArray = stream.toByteArray();
    
    
    //convert your byteArray into bitmap
    Bitmap yourCompressBitmap = BitmapFactory.decodeByteArray(byteArray,0,byteArray.length);
    return yourCompressBitmap;
}

It's all done without losing image dimension.

Any issue please comment below

-8

i think you use this method to compress the bitmap

BitmapFactory.Option imageOpts = new BitmapFactory.Options ();
imageOpts.inSampleSize = 2;   // for 1/2 the image to be loaded
Bitmap thumb = Bitmap.createScaledBitmap (BitmapFactory.decodeFile(photoPath, imageOpts), 96, 96, false);
Opiatefuchs
  • 9,800
  • 2
  • 36
  • 49
Md Hussain
  • 411
  • 4
  • 10