安卓 - 减小图片文件大小

60
我有一个URI图片文件,并想缩小它的大小以便上传。初始图像文件大小因手机而异(可能是2MB,也可能是500KB),但我希望最终大小约为200KB,以便上传。
根据我的了解,我至少有两种选择:
  • 使用BitmapFactory.Options.inSampleSize对原始图像进行子采样并获取较小的图像;
  • 使用Bitmap.compress指定压缩质量来压缩图像。
哪种是最好的选择?
我考虑将图像宽度/高度最初调整到大于1000px(例如1024x768或其他尺寸),然后通过逐渐减小质量来压缩图像,直到文件大小大于200KB。以下是一个示例:
int MAX_IMAGE_SIZE = 200 * 1024; // max final file size
Bitmap bmpPic = BitmapFactory.decodeFile(fileUri.getPath());
if ((bmpPic.getWidth() >= 1024) && (bmpPic.getHeight() >= 1024)) {
    BitmapFactory.Options bmpOptions = new BitmapFactory.Options();
    bmpOptions.inSampleSize = 1;
    while ((bmpPic.getWidth() >= 1024) && (bmpPic.getHeight() >= 1024)) {
        bmpOptions.inSampleSize++;
        bmpPic = BitmapFactory.decodeFile(fileUri.getPath(), bmpOptions);
    }
    Log.d(TAG, "Resize: " + bmpOptions.inSampleSize);
}
int compressQuality = 104; // quality decreasing by 5 every loop. (start from 99)
int streamLength = MAX_IMAGE_SIZE;
while (streamLength >= MAX_IMAGE_SIZE) {
    ByteArrayOutputStream bmpStream = new ByteArrayOutputStream();
    compressQuality -= 5;
    Log.d(TAG, "Quality: " + compressQuality);
    bmpPic.compress(Bitmap.CompressFormat.JPEG, compressQuality, bmpStream);
    byte[] bmpPicByteArray = bmpStream.toByteArray();
    streamLength = bmpPicByteArray.length;
    Log.d(TAG, "Size: " + streamLength);
}
try {
    FileOutputStream bmpFile = new FileOutputStream(finalPath);
    bmpPic.compress(Bitmap.CompressFormat.JPEG, compressQuality, bmpFile);
    bmpFile.flush();
    bmpFile.close();
} catch (Exception e) {
    Log.e(TAG, "Error on saving file");
}

有更好的方法吗?我应该尝试继续使用所有2个方法还是只用其中一个?谢谢


源图像是 PNG 还是 JPG? - SathishBabu S
3个回答

31

使用Bitmap.compress()方法,你只需要指定压缩算法,但是要注意,压缩操作需要花费大量时间。如果你需要调整尺寸以减少图像的内存分配,那么你需要使用Bitmap.Options对图像进行缩放,首先计算位图边界,然后将其解码为指定的大小。

我在StackOverflow上找到的最好的示例是这个


3
我的第一个想法是通过迭代缩放图像(使用inSampleSize)直到图像文件大小小于MAX_IMAGE_SIZE,但是,由于我必须将位图保存到文件中,我必须使用Bitmap.compress() (或者可能有不同的方法来将其保存到文件中?),并且以100%质量调用它时,图像的大小会大于MAX_IMAGE_SIZE,因此我仍然需要迭代Bitmap.compress()(降低质量),直到文件大小< MAX_IMAGE_SIZE。也许有更好的解决方案吗? - KitKat

1
我找到的大部分答案都只是一些碎片,我必须将它们组合起来才能得到一个可用的代码,下面是发布的代码
 public void compressBitmap(File file, int sampleSize, int quality) {
        try {
           BitmapFactory.Options options = new BitmapFactory.Options();
            options.inSampleSize = sampleSize;
            FileInputStream inputStream = new FileInputStream(file);

            Bitmap selectedBitmap = BitmapFactory.decodeStream(inputStream, null, options);
            inputStream.close();

            FileOutputStream outputStream = new FileOutputStream("location to save");
            selectedBitmap.compress(Bitmap.CompressFormat.JPEG, quality, outputStream);
            outputStream.close();
            long lengthInKb = photo.length() / 1024; //in kb
            if (lengthInKb > SIZE_LIMIT) {
               compressBitmap(file, (sampleSize*2), (quality/4));
            }

            selectedBitmap.recycle();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

2个参数sampleSize和quality起着重要作用

sampleSize用于对原始图像进行子采样并返回较小的图像,即
SampleSize == 4返回宽度/高度为原始图像的1/4的图像。

quality用于提示压缩器,输入范围在0-100之间。0表示为小尺寸压缩,100表示为最大质量压缩。


显然这还没有完成。变量“photo”甚至没有在本地声明。此外,它使用递归,这不是一个好主意。 - Shroud
@Pawan,你在分享方面有什么困难吗?我可以帮助你。 - Mightian
@war_Hero:我已经完成了,我的代码现在已经上线了。谢谢兄弟 :-) 保重 - Pawan
2
@Pawan,你能分享你的工作代码给我们吗?这样未来的访问者就可以看一下具体实现了。 - kilokahn
变量 "photo" 是什么? - Ravi Vaniya
显示剩余6条评论

-1

你的回答如何改进之前被接受的答案? - Peter

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接