Android - 减少图片尺寸不会以相同的比例缩小尺寸

时间:2017-04-18 00:15:25

标签: android bitmap image-resizing android-bitmap

我有一个小方法,用于调整图像大小(存储为文件)以生成具有某些预定义文件大小的新图像(在我的情况下为512 KB)。该方法如下所示:

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);
}

当此方法运行时,图像尺寸会缩小到精确的大小(就像我预期的那样),但KB中的图像尺寸不会按相同因子缩小。例如,运行此方法后,我得到以下数字

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

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

我不明白,从图像中删除了这么多像素后,占用这么多空间的是什么,以便保持图像文件的大小。试图查看其他StackOverflow问题和android BitmapFactory和Bitmap类文档,但这个问题无处可见。任何帮助将不胜感激!

2 个答案:

答案 0 :(得分:1)

发生这种情况的原因有很多,这取决于您正在阅读的图像类型(JPG,GIF,PNG,BMP),压缩的质量(如果是JPG),以及在缩小图像尺寸后如何保存它。

在您的示例中,您没有显示您使用的图像或图像使用的压缩类型。但是从代码中我看到你将它保存为PNG图像。

如果你拍摄出高质量的2MB jpg图像,并且像你一样缩小尺寸,但将其保存为PNG(失去质量),你可能会得到比以前更大的文件。

JPG图像使用压缩算法来存储尽可能与原始图像相似的图像,但占用的空间更少。根据质量的不同,您可以获得几乎与原始文件大小相同的图像,或者只需几千字节的可怕图像。

当您将图像保存为PNG时,您将获得与原始图像完全相同的图像(这就是为什么它被称为“无损”),这样,您就无法通过压缩来节省大量文件大小。

如果您使用PNG作为输入,并将缩小版本保存为PNG,您可能会更接近“比例文件大小”的想法。虽然减小尺寸可以通过抗锯齿在结果图像中创建新的颜色,并稍微降低压缩性能。

使用JPG图像会使原始图像中使用的质量和用于压缩结果的质量更加难以预测,这两者都会影响结果的文件大小。

答案 1 :(得分:0)

如果图像未压缩,您可以预期图像文件大小与图像宽度x高度成比例。仅适用于BMP格式。

您正在处理以PNG格式存储的图像。 PNG使用压缩来存储图像。这就是为什么图像大小更多地取决于您的图像细节有多少,而图像宽度和高度更少。

在您的情况下,图像大小最依赖于第二个参数,质量。

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

您可以将其更改为0以使文件更小。

image