欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android图片压缩以及优化实例

程序员文章站 2023-12-10 19:55:34
前言 图片压缩在android技术中已经属于烂大街,上周看了2个开源库然后对自己项目的压缩做了对比,发现一些新东西,记录与此。 为何要压缩 1、体积的原因 如果...

前言

图片压缩在android技术中已经属于烂大街,上周看了2个开源库然后对自己项目的压缩做了对比,发现一些新东西,记录与此。

为何要压缩

1、体积的原因

如果你的图片是要准备上传的,那动辄几m的大小肯定不行的,况且图片分辨率大于设备分辨率的话毫无意义。

2、内存原因

如果图片要显示下android设备上,imageview最终是要加载bitmap对象的,就要考虑单个bitmap对象的内存占用了,如何计算一张图片的加载到内存的占用呢?其实就是所有像素的内存占用总和:

bitmap内存大小 = 图片长度 x 图片宽度 x 单位像素占用的字节数

起决定因素就是最后那个参数了,bitmap'常见有2种编码方式:argb_8888和rgb_565,argb_8888每个像素点4个byte,rgb_565是2个byte,一般都采用argb_8888这种。那么常见的1080*1920的图片内存占用就是:

1920 x 1080 x 4 = 7.9m

压缩原理

从上面可以总结出,图片压缩应该从两个方面入手同时进行:先是降低分辨率,然后降低每个像素的质量也就是内存占用。

分辨率压缩

假设有张原图是3840x2400,我想压缩成1920x1080,实际是不可能100%能压缩这个值的。因为图片压缩要保证宽高比,试想一下800x100的横向图可能压成20x200竖向图吗? 不可能的.。这里常见的算法就是在1920x1080的范围内保证较短边,然后按照比例压缩整个图:

这里原图的宽高比是 3840/2400 = 1.6,目标图的宽高比是1920/1080 = 1.78>1.6,较短边是高。所以就应该按照高的比例来压缩。

2400/1080=2.22,这样真实目标值就是:1728x1080,压缩比四舍五入是:2,然后通过下面代码进行压缩:

 private bitmap compresspixel(string filepath){
  bitmap bmp = null;
  bitmapfactory.options options = new bitmapfactory.options();
  //setting insamplesize value allows to load a scaled down version of the original image
  options.insamplesize = 2;

  //injustdecodebounds set to false to load the actual bitmap
  options.injustdecodebounds = false;
  options.intempstorage = new byte[16 * 1024];
  try {
   //load the bitmap from its path
   bmp = bitmapfactory.decodefile(filepath, options);
   if (bmp == null) {

    inputstream inputstream = null;
    try {
     inputstream = new fileinputstream(filepath);
     bitmapfactory.decodestream(inputstream, null, options);
     inputstream.close();
    } catch (filenotfoundexception exception) {
     exception.printstacktrace();
    } catch (ioexception exception) {
     exception.printstacktrace();
    }
   }
  } catch (outofmemoryerror exception) {
   exception.printstacktrace();
  }finally {
   return bmp;
  }
 }

看起来没什么问题,看看实测结果,原图 3840*2400,大小2.2m,我选4个分辨率当做目标值来压缩:

Android图片压缩以及优化实例

可以看出压缩后的4张图没有一张达到目标值,而且偏差较大,原因就是options.insamplesize这个属性,他只能是2的n次方,如果算出来是7,android会取近似值8,以此类推导致这个值不能压缩到目标值。看了一下compressor这个开源库他对此做了处理,把压缩后的图片在canvas上面按照目标尺寸重绘,得到一个新的bitmap:

核心代码:

matrix scalematrix = new matrix();
  scalematrix.setscale(ratiox, ratioy, 0, 0);

  canvas canvas = new canvas(scaledbitmap);
  canvas.setmatrix(scalematrix);
  canvas.drawbitmap(bmp, 0, 0, new paint(paint.filter_bitmap_flag));

用compressor开源库压缩的图片对比下:

Android图片压缩以及优化实例

可以看出每次都能压缩到真实目标值。(注意不是目标值,注意区分目标值和真实目标值)

质量压缩

bitmap有个方法 compress(compressformat format, int quality, outputstream stream),quality就是压缩质量传入0-100,数值越小压缩的越厉害。

不过我们一般不直接设置这个数值,而是自定义一个压缩后大小比如300kb,然后动态计算这个quality,核心代码:

//进行有损压缩
bytearrayoutputstream baos = new bytearrayoutputstream();
int options_ = 100;
actualoutbitmap.compress(bitmap.compressformat.jpeg, options_, baos);
//质量压缩方法,把压缩后的数据存放到baos中 (100表示不压缩,0表示压缩到最小)
int baoslength = baos.tobytearray().length;
while (baoslength / 1024 > maxfilesize)
 {
//循环判断如果压缩后图片是否大于maxmemmorrysize,大于继续压缩 
baos.reset();
//重置baos即让下一次的写入覆盖之前的内容
 options_ = math.max(0, options_ - 10);//图片质量每次减少10
 actualoutbitmap.compress(bitmap.compressformat.jpeg, options_, baos);
//将压缩后的图片保存到baos中 
baoslength = baos.tobytearray().length;
 if (options_ == 0)//如果图片的质量已降到最低则,不再进行压缩 
break;
}

压缩实践

目前成熟的开源库有luban:https://github.com/curzibn/luban

这个开源库算法比较复杂,根据效果图前后对比逆向推算了微信朋友圈的压缩,最后效果和微信差不多,如果你对压缩要求很高可以使用这个。不过方法调用是异步的,回调形式反馈结果,这个不太好。。

compressor:https://github.com/zetbaitsu/compressor

这个开源库就是在普通的压缩算法上做了优化改进,源码很容易看懂,推荐!下面是用compressor对三张大图不同目标值做的压缩测试(bv是我们项目的压缩,忽略就好),质量参数设的是80%

Android图片压缩以及优化实例

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。