要了解图片压缩首先要了解一些图片相关的其他概念
点阵图和矢量图
1. 点阵图(位图)或者叫 像素图
构成点阵图的最小单位是象素,位图就是由象素阵列的排列来实现其显示效果的,每个象素有自己的颜色信息。所以在对位图图像进行编辑操作的时候,可操作的对象是每个象素,可以通过改变图像的色相、饱和度、明度,从而改变图像的显示效果
总结:点阵图缩放会失真
2. 矢量图(向量图)
矢量图每一点上纪录的元素形状及颜色的算法,而不是像素信息,在打开矢量图的时候,软件对图形象对应的函数进行运算,将运算结果 即(图形的形状和颜色)显示出来。无论显示画面是大还是小,画面上的对象对应的算法是不变的,so, 矢量图无论放大缩小其显示效果都一样
总结:图片任意放大或缩小都不会失真
颜色
计算机在表示颜色的时候,有两种形式,一种称作索引颜色(Index Color),一种称作直接颜色(Direct Color)。
索引色
用一个数字来代表(索引)一种颜色,在存储图片的时候,存储一个数字的组合,同时存储数字到图片颜色的映射。这种方式只能存储有限种颜色,通常是256种颜色,对应到计算机系统中,使用一个字节的数字来索引一种颜色。
直接色
使用四个数字来代表一种颜色,这四个数字分别代表这个颜色中红色、绿色、蓝色以及透明度。现在主流显示设备可以在这四个维度分别支持256种变化,所以直接色可以表示2的32次方种颜色。当然并非所有的直接色都支持这么多种,为压缩空间使用,有可能只有表达红、绿、蓝的三个数字,每个数字也可能不支持256种变化之多。
压缩方式
有损压缩
指在压缩文件大小的过程中会损失了一部分图片的信息,也即降低了图片的质量,并且这种损失是不可逆的。
无损压缩。
指在压缩文件大小的过程中图片的质量没有任何损耗。无损压缩过的图片中恢复出原来的信息
图片格式
常见的图片格式有 bmp、gif、png、jpeg(jpg)、webp 等6种格式
BMP (BitMap简称)
是无损的、既支持索引色也支持直接色的、点阵图,这种图片格式几乎没有对数据进行压缩,所以bmp格式的图片文件通常比较大。现在在Windows操作系统中比较常见,其他地方不常见
GIF (Graphics Interchange Format)
是无损的、采用索引色的、点阵图。使用GIF格式保存图片时不会降低图片质量。
优点:1.由于数据的压缩,GIF格式的图片要远小于BMP格式的图片,
2.GIF格式还具有支持动画以及透明的优点。
缺点:GIF格式仅支持8bit的索引色,即在整个图片中,只能存在256种不同的颜色。
JPEG(Joint Photographic Experts Group)
JPEG是有损的、采用直接色的、点阵图。JPEG图片格式的设计目标,是在不影响人类可分辨的图片质量的前提下,尽可能的压缩文件大小。也就是说JPEG去掉了一部分图片的原始信息,即是进行了有损压缩
优点:采用了直接色,色彩更丰富
缺点:有损的
WebP
WebP是谷歌开发的一种新图片格式,WebP是同时支持有损和无损压缩的、使用直接色的、点阵图。
WebP具有更小的文件体积。这种图片格式在pc 端应用更广泛,因为图片体积小,可以将大大减少浏览器和服务器之间的数据传输量,进而降低访问延迟,提升访问体验。
在无损压缩的情况下,相同质量的WebP图片,文件大小要比PNG小26%;
在有损压缩的情况下,具有相同图片精度的WebP图片,文件大小要比JPEG小25%~34%;
PNG(Portable Network Graphics)
PNG-8 是PNG的索引色版本,PNG-8是无损的、使用索引色的、点阵图。
文件体积小,另外PNG-8支持透明度的调节、支持动画,只不过浏览器对这个支持不好,所以应用并不广泛
PNG-24 是PNG的直接色版本,PNG-24是无损的、使用直接色的、点阵图
PNG-24和BMP类似,PNG-24和BMP对比,前者的文件体积更小,但是比JPEG,GIF,PNG-8 都要大
总结
图片压缩原理
质量压缩
原理:质量压缩是通过改变图片的位深和透明度来来减小图片占用的磁盘空间大小。
所以质量压缩不会改变图片在内存中的大小(PS 图片内存大小是根据图片的宽度、高度和一个像素所占用的字节数来计算的,因为质量压缩没有改变高度和宽度,自然内存的大小不会改变)其次质量压缩不会改变分图片的辨率
示例:
/**
* 压缩图片:限制图片的质量,比如需要把5M的图片压缩到2M,可采取此方法
*
* @param filePath 被压缩的图片路径
* @param format 压缩图片格式,jpg,webp
* @param sizeLimit 大小限制 单位是kb
* @return 压缩后的图片
*/
public static Bitmap compressBitmap(String filePath, Bitmap.CompressFormat format, int sizeLimit) {
if (TextUtils.isEmpty(filePath)) {
return null;
}
try {
Bitmap bm = BitmapFactory.decodeFile(filePath);
ByteArrayOutputStream byteAos = new ByteArrayOutputStream();
// 质量压缩方法,这里100表示不压缩,把压缩后的数据存放到baos中
int quality = 100;
bm.compress(format, quality, byteAos);
if (enableLog) {
Log.d(TAG, " 压缩前size = " + byteAos.toByteArray().length / 1024);
}
int minus = 10;
if (byteAos.toByteArray().length / 1024 > 3 * 1024) {
minus = 50;
}
// 循环判断如果压缩后图片是否大于sizeLimit(单位是k),大于继续压缩
while (byteAos.toByteArray().length / 1024 > sizeLimit && quality > 0 && quality <= 100) {
// 重置baos即清空baos
byteAos.reset();
// 这里压缩options%,把压缩后的数据存放到baos中
bm.compress(format, quality, byteAos);
quality -= minus;// 每次都减少10
if (byteAos.toByteArray().length / 1024 > 2 * 1024) {
minus = 20;
} else if (byteAos.toByteArray().length / 1024 > 1024) {
minus = 10;
}
if (enableLog) {
Log.d(TAG, " quality = " + quality);
}
}
if (enableLog) {
Log.d(TAG, " 压缩后size = " + byteAos.toByteArray().length / 1024);
}
byte[] result = byteAos.toByteArray();
byteAos.close();
if (result != null) {
return BitmapFactory.decodeStream(new ByteArrayInputStream(result), null, null);
} else {
return null;
}
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
采样压缩(按比例压缩)
原理:采样率压缩是通过设置BitmapFactory.Options.inSampleSize,通过改变图片的分辨率,进而减小图片所占用的磁盘空间和内存大小。
示例:
computeSize 算法计算
private static int computeSize(int srcWidth, int srcHeight) {
srcWidth = srcWidth % 2 == 1 ? srcWidth + 1 : srcWidth;
srcHeight = srcHeight % 2 == 1 ? srcHeight + 1 : srcHeight;
int longSide = Math.max(srcWidth, srcHeight);
int shortSide = Math.min(srcWidth, srcHeight);
float scale = ((float) shortSide / longSide);
if (scale <= 1 && scale > 0.5625) {
if (longSide < 1664) {
return 1;
} else if (longSide < 4990) {
return 2;
} else if (longSide > 4990 && longSide < 10240) {
return 4;
} else {
return longSide / 1280 == 0 ? 1 : longSide / 1280;
}
} else if (scale <= 0.5625 && scale > 0.5) {
return longSide / 1280 == 0 ? 1 : longSide / 1280;
} else {
return (int) Math.ceil(longSide / (1280.0 / scale));
}
}
[1, 0.5625) 即图片处于 [1:1 ~ 9:16) 比例范围内
[0.5625, 0.5) 即图片处于 [9:16 ~ 1:2) 比例范围内
[0.5, 0) 即图片处于 [1:2 ~ 1:∞) 比例范围内
例如测试机魅族t16屏幕比是4:3 ,那么图片压缩比就是3/4 =0.75,或者用图片的的宽高进行比较也行,, 拍出来的照片是 3024x 3042 3024/4032 =0.75
计算压缩图片的实际文件大小,图片比例越大则文件越大
则图片压缩后的大小就是2268*3024
代码示例:
* 按照比例压缩,按照默认要锁后的比例
*
* @param context 上线文
* @param srcPath 图片原始路径
* @return Uri
*/
public static Uri ratioCompressSaveToGallery(Context context, String srcPath) {
try {
BitmapFactory.Options newOpts = new BitmapFactory.Options();
//开始读入图片,此时把options.inJustDecodeBounds 设回true了
newOpts.inJustDecodeBounds = true;
Bitmap bitmap = BitmapFactory.decodeFile(srcPath, newOpts);//此时返回bm为空
newOpts.inJustDecodeBounds = false;
int w = newOpts.outWidth;
int h = newOpts.outHeight;
if (enableLog) {
Log.d(TAG, "src width = " + w + " height = " + h);
}
int size = computeSize(w, h);
newOpts.inSampleSize = size;//设置缩放比例
if (enableLog) {
Log.d(TAG, "computeSize = " + size);
}
//重新读入图片,注意此时已经把options.inJustDecodeBounds 设回false了
bitmap = BitmapFactory.decodeFile(srcPath, newOpts);
ByteArrayOutputStream byteAos = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 60, byteAos);
byte[] result = byteAos.toByteArray();
Bitmap resultBitMap = BitmapFactory.decodeStream(new ByteArrayInputStream(result), null, null);
if (enableLog) {
Log.d(TAG, "result width = " + resultBitMap.getWidth() + " height = " + resultBitMap.getHeight());
}
bitmap.recycle();
if (result != null) {
Uri uri = saveImageToGallery(context, result);
if (uri != null) {
return uri;
} else {
return null;
}
} else {
return null;
}
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
图片库对比
目前流行的开源库对比
tiny官方图片压缩效果对比详情
github 地址:https://github.com/Sunzxyong/Tiny
luban 官方图片压缩效果对比详
gitHub地址:https://github.com/Curzibn/Luban
真机测试
魅族16th和OPPO findx 图片压缩对比参照
Luban 框架优缺点
优点:
是根据微信图片算法你推算的,适用真机测试丰富
缺点:
1,当没有设定压缩路径时,抛异常无闪退
2,源码中,压缩比率固定住60,无法修改
3,压缩配置,参数不太适应真实项目需求
4,不能指定压缩大小,比如100kb 以内
5,内部封装已AsyncTasky异步的图片压缩,对RxJava的支持不好
tiny 框架的优缺点
优点:
1,支持批量压缩
2,libjpeg-turbo的价值是利用SIMD指令集,加速了编解码过程,时间缩短1/3
3, 解码的时候是由内部分配,不会造成资源浪费
缺点:
需要导入so库导致包的体积会变大,
android 7.0一下的手机压缩后比android7.0以上的手机压缩大小会偏大一点