简体   繁体   中英

Android - reducing Image dimensions does not reduce size in the same proportion

I have a small method that is meant to resize an image (stored as a File) to produce a new image with some predefined file size (in my case 512 KB). The method looks something like the following :

private static final int maximumFileSizeInKBs = 512;

public static Uri resizeImage(Context context, Uri imageUri) {
    // get image width and height
    BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    Bitmap imageBitmap = BitmapFactory.decodeFile(new File(imageUri.getPath()).getAbsolutePath(), options);
    int imageWidth = options.outWidth;
    int imageHeight = options.outHeight;

    // get image file size (in KBs)
    File file = new File(imageUri.getPath());
    int fileSizeInKBs = Integer.parseInt(String.valueOf(file.length()/1024));

    // get the scaling factor
    // (square root because when we scale both the width and height by the
    // square root of the scaling factor, then the size of the final image
    // will be scaled by the scaling factor)
    double scalingFactor =  Math.sqrt(fileSizeInKBs / maximumFileSizeInKBs);

    // no need to scale if file already within maximum file size limit
    if(scalingFactor <= 1) {
        return imageUri;
    }

    // get scaled dimensions
    int scaledImageWidth = (int) Math.floor(imageWidth / scalingFactor);
    int scaledImageHeight = (int) Math.floor(imageHeight / scalingFactor);

    // load original bitmap (load a scaled copy to avoid OutOfMemory errors)
    options = new BitmapFactory.Options();
    options.inSampleSize = Math.max(imageHeight/scaledImageWidth, imageHeight/scaledImageHeight);
    imageBitmap = BitmapFactory.decodeFile(new File(imageUri.getPath()).getAbsolutePath(), options);

    // the scaled image above will be scaled by a value equal to the
    // nearest power of 2, hence it wont be scaled to the exact value
    // that we want. So, we need to calculate new scaling factors
    // based on the scaled loaded bitmap
    Matrix m = new Matrix();
    RectF inRect = new RectF(0, 0, imageWidth, imageHeight);
    RectF outRect = new RectF(0, 0, scaledImageWidth, scaledImageHeight);
    m.setRectToRect(inRect, outRect, Matrix.ScaleToFit.CENTER);
    float[] values = new float[9];
    m.getValues(values);

    // create a scaled bitmap with required file size
    Bitmap scaledBitmap = Bitmap.createScaledBitmap(imageBitmap, (int) (imageWidth*values[0]), (int) (imageHeight*values[4]), true);

    // delete original image
    new File(imageUri.getPath()).delete();

    // write bitmap to file
    File newImageFile = BaseResourceClass.makeTempResourceFile(Slide.ResourceType.IMAGE, context);
    try {
        newImageFile.createNewFile();
        FileOutputStream fos = new FileOutputStream(newImageFile);
        scaledBitmap.compress(Bitmap.CompressFormat.PNG, 1, fos);
        fos.close();
    } catch (IOException e) {
        e.printStackTrace();
        Toast.makeText(context, "Something went wrong", Toast.LENGTH_SHORT).show();
    }

    // recycle bitmaps to reallocate memory
    Storage.recycleBitmap(imageBitmap);
    Storage.recycleBitmap(scaledBitmap);

    // scaling done, return new Image
    return Uri.fromFile(newImageFile);
}

When this method runs, the image dimensions get reduced to the exact size (as in what I expect it to be), but the image size in KB's is not scaled down by the same factor. For example, after running this method I am getting the following numbers

before resizing
imageWidth : 3120, imageHeight : 4160, fileSize : 2960 KB

after resizing 
imageWidth : 1395, imageHeight : 1860, fileSize : 2491 KB

I don't understand that after removing so many pixels from the image, what is it that is taking up so much space so as to keep the image file size this much. Tried to look at other StackOverflow questions and android BitmapFactory and Bitmap classes documentation, but with this problem being mentioned nowhere. Any help would be appreciated!

There are a lot of reasons why this happens, and it will depend on the type of image you are reading (JPG, GIF, PNG, BMP), the quality with which it has been compressed (if it was JPG), and how you are saving it after reducing image dimensions.

In your example you are not showing what image you used or what type of compression the image was using. But from the code I see you are saving it as a PNG image.

If you take a great quality 2MB jpg image, and you reduce the size like you did, but save it as a PNG (loseless quality), you will probably end up with a bigger file than before.

JPG images uses compression algorithms to store an image as similar as possible to the original but taking up much less space. Depending on quality, you can get an image that looks almost as the original with a big filesize, or a horrible image that takes just a few kilobytes.

When you save an image as PNG, you get exactly the same image as your original (that's why it's called loseless), and that way, you can't save much filesize through compression.

If you use a PNG as an input, and save a reduced version as PNG, you will probably be much closer to your "proportional filesize" idea. Although reducing the size can create new colors in the resulting image through antialiasing and reduce compression performance a bit.

Working with JPG images makes it a bit more unpredictable as the quality that was used in the original image and the one you use to compress the result, both impacts result's filesize.

You can expect your image file size be proportional to image width x height if image is not compressed. It is true for BMP format only.

You are processing images that are stored in PNG format. PNG is use compression to store images. That is why image size depend more on how much details you image has and less on image width&height.

In your case image size depend most on second parameter, quality.

scaledBitmap.compress(Bitmap.CompressFormat.PNG, 1 , fos);

You can change it to 0 to have smaller file.

图片

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM