5

I've implemented the below code to scale an image down, with respect to the aspect ratio (by proportionally decreasing height/width with respect to one another).. This will definitely help to reduce the size of the images uploaded to my backend, but this does not take into account the resolution of the images. I want to set a hard image limit, say 800Kb, and if the image is greater than 800Kb after resizing, then compress to a point at which it is less than 800Kb.

Anyone have any experience doing something like this? I'm curious what relationship lies between the quality argument passed into the Bitmap.Compress method and how much file size is shaven off per percentage quality - If i could obtain this information, I believe I can reach my goal.

Thanks for any help ahead of time, my current code is below, maybe it will help others heading in this direction in the future.

public static void uploadImage(String url, File file, Callback callback, Context context,
                               IMAGE_PURPOSE purpose) {

    BitmapFactory.Options options = new BitmapFactory.Options();
    options.inPreferredConfig = Bitmap.Config.ARGB_8888;
    Bitmap bitmap = BitmapFactory.decodeFile(file.getAbsolutePath(), options);

    int maxWidth = 0;
    int maxHeight = 0;
    int maxSize = 0;
    switch(purpose){
        case PROFILE:
            maxWidth = Constants.MAX_PROFILE_IMAGE_WIDTH;
            maxHeight = Constants.MAX_PROFILE_IMAGE_HEIGHT;
            maxSize = Constants.MAX_PROFILE_IMAGE_SIZE;
            break;
        case UPLOAD:
            maxWidth = Constants.MAX_UPLOAD_IMAGE_WIDTH;
            maxHeight = Constants.MAX_UPLOAD_IMAGE_HEIGHT;
            maxSize = Constants.MAX_UPLOAD_IMAGE_SIZE;
            break;
    }

    int newWidth = bitmap.getWidth();
    int newHeight = bitmap.getHeight();

    // Make sure the width is OK
    if(bitmap.getWidth() > maxWidth){

        // Find out how much the picture had to shrink to get to our max defined width
        float shrinkCoeff = ((float)(bitmap.getWidth() - maxWidth) / (float)bitmap.getWidth());
        newWidth = maxWidth;

        // Shrink the height by the same amount to maintain aspect ratio
        newHeight = bitmap.getHeight() - (int)((float)bitmap.getHeight() * shrinkCoeff);
    }

    // Make sure the height is OK
    if(newHeight > maxHeight){

        // Find out how much the picture had to shrink to get to our max defined width
        float shrinkCoeff = ((newHeight - maxHeight) / newHeight);
        newHeight = maxHeight;

        // Shrink the width by the same amount to maintain aspect ratio
        newWidth = newWidth - (int)((float)newWidth * shrinkCoeff);
    }

    Bitmap resized = Bitmap.createScaledBitmap(bitmap, newWidth, newHeight, true);

    // Get the image in bytes
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    resized.compress(Bitmap.CompressFormat.JPEG, 100, bos);
    byte[] imageBytes = bos.toByteArray();

    // If the size on disk is too big, reduce the quality
    if(imageBytes.length > maxSize){
        // Compress image here to get to maxSize
    }
Riptyde4
  • 5,134
  • 8
  • 30
  • 57
  • can please refer http://voidcanvas.com/whatsapp-like-image-compression-in-android/ – Vadivel Nov 21 '16 at 04:38
  • @Vadivel Can you give me some more info, such as what to refer to and why? The developer in this blog post seems to be doing a more complicated version of what I'm doing, achieving the same result. Also, there is no information about reduction of actual resolution (e.g. pixels per inch, not height X width). I am not looking to preserve quality, I am looking to reduce it in the interest of saving space. – Riptyde4 Nov 21 '16 at 04:45
  • @Vadivel After reviewing quite a bit, I do see why this developer has chosen the smarter path with regards to risk of an out of memory error dealing with large files - I've taken this into account, so thanks for that reference. However, I'm still looking for a way to measure/decrease pixel density in the image with respect to a hard cap on image size in bytes. – Riptyde4 Nov 21 '16 at 05:38
  • you can resize based on height and width – Vadivel Nov 21 '16 at 05:39
  • This may help: https://stackoverflow.com/questions/37299490/android-reduce-file-size-for-camera-captured-image-to-be-less-than-500-kb/38238757#38238757 – i_A_mok Nov 21 '16 at 05:47
  • @I_A_Mok That's great thanks - good reference if I end up having to do it that way. What I'm really looking for is a numerical relationship (if one exists/is known) between one percentage of quality and the resulting file size, so that I don't have to repeatedly compress in a loop and check the resulting size each time until the sufficient size is obtained – Riptyde4 Nov 21 '16 at 06:00
  • Ex. How much would a 2MB JPG shrink if I called Bitmap.CompressFormat with 80 percent quality specified, a 20% reduction of quality? – Riptyde4 Nov 21 '16 at 06:17
  • I have searched it before but could not find an answer. It is becasue the resulting file size is depending on the characteristics of the bitmap (contains large area or not). Some posts suggest to start trying with 75% if not much concern with bitmap quality. You may need to test with your own sample pictures. – i_A_mok Nov 21 '16 at 06:33
  • And after all these resizing you will upload the result base64 encoded? – greenapps Nov 21 '16 at 10:19
  • `but this does not take into account the resolution of the images.` Dont understand. What do you mean by that? Just resize every picture to a predetermined resolution and you are done. If the original resolution is equal or smaller then do nothing. – greenapps Nov 21 '16 at 10:21
  • @greenapps no, not using base64 – Riptyde4 Nov 21 '16 at 18:39
  • @greenapps I am referring to resolution as pixel density or pixels per inch, different from the dimensions (height/width) – Riptyde4 Nov 21 '16 at 18:40
  • It threat is duplicated but [Here](https://meta.stackexchange.com/q/104227) is my solution – San Juan Sep 20 '17 at 23:54

0 Answers0