Android 图片压缩

先吹会逼。这俩月一直在磨叽工作的事情,也没时间静下心来搞点事。从9月初到现在10月底,现在到新公司俩星期了。首先庆贺一下我终于来到魔都了,希望自己能迅速适应新的环境,还有就是技术水平更上一层楼。然后,没有然后了。

一、概述

最近,领导给我提了一个需求,就是我们现在的项目经常需要用到拍照上传的功能,差不多一天少说20多张吧,如果一张照片500k来算,一天10M,一个月也得300M,这还不算其他的网络请求的流量消耗。以现在的用户流量来说,不算太大问题,但是也是坑爹的一笔。所以,给出要求,压缩到200K左右,但是要能看的清楚上面的文字。

以目前的手机拍照来说,随便来个5M以上的图片都是小意思,而且用户也不会刻意去改一些拍照配置,原图就很大。我以前做的几个项目,基本上用到上传图片的都是头像,一般随意对尺寸压缩一下就OK了,基本上500k左右。

但是这样的压缩可能效果不太如意,仅仅是尺寸上的压缩,如果想要更佳的效果可能就要多管齐下了。总的来说,bitmap内存大小 = 图片的width * 图片的 height * 每像素占用的字节。从这三个角度考虑基本上就可以达到需要的压缩效果。但是对于要保存到文件又是另一回事了。

二、Bitmap压缩

1.尺寸压缩

直接甩代码了:

/**
 * 从文件获取压缩尺寸的图片
 *
 * @param imgPath 图片路径
 * @param targetW 目标width
 * @param targetH 目标height
 */
public static Bitmap compress(String imgPath, float targetW, float targetH) {
    BitmapFactory.Options options = new BitmapFactory.Options();
    // 开始读入图片,此时把options.inJustDecodeBounds 设回true,即只读边不读内容
    options.inJustDecodeBounds = true;
    options.inPreferredConfig = Bitmap.Config.RGB_565;
    // 此时decode获得的只是图片的一些详细信息,并不含图片内容
    BitmapFactory.decodeFile(imgPath, options);
    int w = options.outWidth;
    int h = options.outHeight;
    // 缩放比。由于是固定比例缩放,只用高或者宽其中一个数据进行计算即可
    int inSampleSize = 1;//be=1表示不缩放
    if (w > h && w > targetW) {//如果宽度大的话根据宽度固定大小缩放
        inSampleSize = (int) (options.outWidth / targetW);
    } else if (w < h && h > targetH) {//如果高度高的话根据宽度固定大小缩放
        inSampleSize = (int) (options.outHeight / targetH);
    }
    if (inSampleSize <= 0) inSampleSize = 1;
    options.inSampleSize = inSampleSize;//设置缩放比例
    // 开始压缩图片,注意此时把options.inJustDecodeBounds 设为false
    options.inJustDecodeBounds = false;
    // 压缩好比例大小后再进行质量压缩
    return BitmapFactory.decodeFile(imgPath, options);
}

这个代码大部分人都见过,就是通过inJustDecodeBounds 先获取图片信息,之后再加载到内存中,这样,既缩小了图片尺寸,也压缩了图片大小,注意这里采用了RGB_565的颜色模式,具体看下面一个。

2. 像素的颜色表示

这个其实也是对解码时的Options进行配置,以决定采用哪种颜色模式。在Bitmap.Config中主要有5中模式:

  • ALPHA_8:该模式只存储像素的透明度,一般用于存储mask,每个像素占一个字节;
  • RGB_565:保存RGB三个通道,当使用不需要高色彩保真度的不透明位图时,此配置很有用,rgb共占5+6+5是16位,即每个像素占两个字节;
  • ARGB_4444:保存ARGB四个通道,共占16位,两字节。但是由于图片质量效果很差,从Android4.4开始就弃用了,推荐是使用ARGB_8888;
  • ARGB_8888:ARGB各占8位,即每个像素占4个字节,质量最好,但是占用内存也较高;
  • RGBA_F16:这个每个像素存储占8个字节,此配置特别适用于宽色域和HDR内容。但是一般不会用的。

虽然有5种,但是最常用的也就俩,如果对图片解码质量要求高,就使用ARGB_8888,如果不是很高,且不在乎透明度,则使用RGB_565,比前者节省一半的内存。使用方法很简单:

BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.RGB_565;
BitmapFactory.decodeFile(imgPath, options);

上述的两个方法是针对Bitmap在内存中的大小进行压缩。除此之外还有Matrix、Bitmap.createScaledBitmap()等方法进行压缩,都是针对其尺寸或者像素占用字节数来进行压缩的,这个就不多说了。

三、图片质量压缩

/**
 * 循环压缩并保存
 * <p>
 * 注意,当大于指定大小时再次压缩,直到小于指定大小。
 * 但是由于考虑到图片的效果,最低只会压缩到quality为10
 *
 * @param image   要压缩的图片
 * @param outPath 要保存的压缩后图片位置
 * @param maxSize 最大尺寸
 */
public static void compressLoop2File(Bitmap image, String outPath, int maxSize) throws FileNotFoundException {
    ByteArrayOutputStream os = new ByteArrayOutputStream();
    int quality = 100;
    image.compress(Bitmap.CompressFormat.JPEG, quality, os);
    // 循环压缩
    while (os.toByteArray().length / 1024 > maxSize && quality > 10) {
        os.reset();
        quality -= 10;
        image.compress(Bitmap.CompressFormat.JPEG, quality, os);
    }

    // 生成压缩后的文件
    FileOutputStream fos = new FileOutputStream(outPath);
    try {
        fos.write(os.toByteArray());
        fos.flush();
        fos.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

质量压缩很简单,通过Bitmap的compress方法,即可实现对图片的质量压缩效果。但是如果我们将ByteArrayOutputStream解码为bitmap,bitmap与之前比大小不变,其相当于没有压缩,因为图像的长宽与没像素所占内存都没有变化,那么图片大小肯定不会变化。

但是既然叫质量压缩,肯定是有变化的。真正被压缩的其实是位深及透明度,这样不影响图片大小与像素,bitmap大小不变,但是压缩后保存到的ByteArrayOutputStream实例的大小表明图片确实是被压缩了。

这种压缩主要适用于将图片以二进制形式存储以及在网络上传递等操作。

四、综述

综上所述的方法,我做了个测试,在保证图片效果的前提下,采用先压尺寸再质量压缩的方式,把一张图片尺寸由5152×2896缩小为1288×724,而图片文件大小由4.02M压缩为163k,保证效果的前提下其实还能再压缩,大家可以试试效果。

其实图片的相关处理并不仅仅是压缩的这些,还有图片的灰度处理,图片的饱和度、色调、亮度等的调整,网上可以轻易查找到。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值