Android图片压缩必备基础知识

168 阅读5分钟

前言

一、图片基础知识
  1. 什么是AGRB
  2. 什么是bitmap
  3. 色彩模式
  4. 位深和色深
  5. 内存中bitmap的大小
二、Android中图片压缩的方法介绍
  1. 质量压缩
  2. 尺寸压缩

图片基础知识:

  • ARGB介绍:最常见的颜色模型,设备相关,四种通道,取值[0,255]。A:透明度,rgb 分别代表红蓝绿。
  • bitmap概率:本质是一张图片内容在手机内存的表现形式。 它将图片内存的像素点存储起来;每个像素点存储该像素点的ARGB值。
  • 色彩模式:Bitmap.config是bitmap的枚举类,它表示的就是每个像素点对ARGB通道值的存储方案。取值有一下四种:

ALPHA_8:每个像素点占8位(1个字节),存储透明度信息,没有颜色信息

RGB565:每个像素点占16位,没有透明度,能表示 2^16种颜色

ARGB_4444:每个像素占16位,能表示2^12种颜色

ARGB_8888:每个像素占32位,能表示2^24种颜色

  • 位深和色深:

色深:色彩的深度,指一个像素点需要多少个像素存储ARGB值。即上述Bitmap.config参数的值指的就是色深

位深:记录图片的颜色时,计算机实际是用每个像素对象二进制值位数来表示的。位深时物理硬件参数,主要用来存储。 举例:一张100100 像素 的图片 色深32位,位深24位,那么:该图片占内存:10010032/8 Byte 在文件中所占大小 100100 *24/8 *压缩率 byte

  1. 一张图片所占内存大小的计算公式:分辨率*像素点大小;但分辨率不一定是原图的分辨率,需要结合场景来说,像素点大小就几种情况:Bitmap.config 对应的值 ARGB4444,ARGB8888....
  2. 如果图片来源是res时,分辨率需要根据设备的dpi和图片所占res的dpi做一次转换,规则如下 新分辨率 = 原图分辨率* (设备dpi/res的dpi)^2
  3. 其他来源的图片,如网络、文件、磁盘、流均按照原图的分辨率来计算图片的内存大小
  4. 使用glide时,如果有设置显示图片的控件,那么会按照控件的大小,降低图片的分辨率加载。图片来源是res的分辨率转换规则对其也是无效的。

图片压缩

质量压缩:

在Android中,对图片进行质量压缩,实现方式如下所示:

byteArrayOutputStream outputStream = new ByteArrayOutputStream();
//quality 为0~100,0表示最小体积,100表示最高质量,对应体积也是最大
bitmap.compress(Bitmap.CompressFormat.JPEG, quality , outputStream);

在上述代码中,我们选择的压缩格式是CompressFormat.JPEG,除此之外还有两个选择:其一,CompressFormat.PNG, PNG格式是无损的,它无法再进行质量压缩,quality这个参数就没有作用了,会被忽略,所以最后图片保存成的文件大小不会有变化; 其二,CompressFormat.WEBP,这个格式是 google 推出的图片格式,它会比 JPEG更加省空间,经过实测大概可以优化 30%左右。 在某些应用场景需要bitmap转换成ByteArrayOutputStream,需要根据你要压缩的图片格式来判断使用CompressFormat.PNG还是Bitmap.CompressFormat.JPEG, 这时候quality为100。

尺寸压缩
  1. 邻近采样法:
BitmapFactory.Options options = new BitmapFactory.Options();
//或者 inDensity 搭配 inTargetDensity 使用,算法和 inSampleSize 一样
options.inSampleSize = 2; //设置图片的缩放比例(宽和高) , google推荐用2的倍数:
Bitmap bitmap = BitmapFactory.decodeFile();
Bitmap compress = BitmapFactory.decodeFile();

在这里着重讲一下这个inSampleSize。从字面上理解,它的含义是: “设置取样大小”。它的作用是:设置inSampleSize的值(int类型)后,假如设为4,则宽和高都为原来的1/4,宽高都减少了,自然内存也降低了。

参考Google官方文档的解释,我们从中可以看到 x(x 为 2 的倍数)个像素最后对应一个像素,由于采样率设置为 1/2,所以是两个像素生成一个像素。 邻近采样的方式比较粗暴,直接选择其中的一个像素作为生成像素,另一个像素直接抛弃,这样就造成了图片变成了纯绿色,也就是红色像素被抛弃。 邻近采样采用的算法叫做邻近点插值算法。 2. 双线性采样法

Bitmap bitmap = BitmapFactory.decodeFile("xxx.png");
Bitmap compress = Bitmap.createScaledBitmap(bitmap, bitmap.getWidth()/2, bitmap.getHeight()/2, true);
或者直接使用 matrix 进行缩放

Bitmap bitmap = BitmapFactory.decodeFile("xxx.png");
Matrix matrix = new Matrix();
matrix.setScale(0.5f, 0.5f);
bm = Bitmap.createBitmap(bitmap, 0, 0, bit.getWidth(), bit.getHeight(), matrix, true);
  • 看源码可以知道 createScaledBitmap 函数最终也是使用第二种方式的 matrix进行缩放,双线性采样使用的是双线性內插值算法,这个算法不像邻近点插值算法一样,直接粗暴的选择一个像素,而是参考了源像素相应位置周围 2x2 个点的值,根据相对位置取对应的权重,经过计算之后得到目标图像。
  • 双线性内插值算法在图像的缩放处理中具有抗锯齿功能, 是最简单和常见的图像缩放算法,当对相邻 2x2 个像素点采用双线性內插值算法时,所得表面在邻域处是吻合的,但斜率不吻合,并且双线性内插值算法的平滑作用可能使得图像的细节产生退化,这种现象在上采样时尤其明显。

双线性采样对比邻近采样的优势在于: 1.它的系数可以是小数,而不一定是整数,在某些压缩限制下,效果尤为明显; 2.处理文字比较多的图片在展示效果上的差别,双线性采样效果要更好