Android 中图片压缩分析(上),彻底帮你搞懂

文章探讨了Android中Skia图像引擎在JPEG压缩中的作用,重点分析了Huffman算法和optimize_coding参数对图片质量和文件体积的影响,以及libjpeg在Android中的使用情况。
摘要由CSDN通过智能技术生成

Log.i(TAG, "onCreate: file.length " + file.length());
fs.flush();
fs.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
//查看压缩之后的 Bitmap 大小
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 50, outputStream);
byte[] bytes = outputStream.toByteArray();
Bitmap compress = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
Log.i(TAG, "onCreate: bitmap.size = " + bitmap.getByteCount() + " compress.size = " + compress.getByteCount());

首先,我们来看看 quality 参数被设置为 50,质量压缩前后的图片对比,可以看到其尺寸大小并没有变化,但是视觉感受也可以明显地看到图片变的模糊了一些。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

通过日志也可以看到,在质量压缩前后图片转成 Bitmap 之后在内存中的大小也并没有变化,这是在保持像素的前提下,改变图片的位深及透明度等:

//压缩之后图片占用的存储体积
compress.length = 7814
//在内存中压缩前后图片占用的大小
bitmap.size = 350000 compress.size = 350000

对比二者,保存前的图片存储体积是 106k,质量设为 50 并且保存为 JPEG 格式之后,图片存储大小就只有 8k 了,并且质量设的越低,保存成文件之后,文件的体积也就越小。

三、Android Skia 图像引擎

在上文中,提到的Skia是Android 的重要组成部分。

Skia 是一个 Google 自己维护的 c++ 实现的图像引擎,实现了各种图像处理功能,并且广泛地应用于谷歌自己和其它公司的产品中(如:Chrome、Firefox、 Android等),基于它可以很方便为操作系统、浏览器等开发图像处理功能。

Skia 在 Android 中提供了基本的画图和简单的编解码功能,可以挂接其他的第三方编码解码库或者硬件编解码库,例如 libpng 和 libjpeg,libgif 等等。因此,这个函数调用bitmap.compress(Bitmap.CompressFormat.JPEG…),实际会调用 libjpeg.so 动态库进行编码压缩。

最终 Android 编码保存图片的逻辑是 Java 层函数→Native 函数→Skia函数→对应第三库函数(例如 libjpeg)。所以 skia 就像一个胶水层,用来链接各种第三方编解码库,不过 Android 也会对这些库做一些修改,比如修改内存管理的方式等等。

Android 在之前从某种程度来说使用的算是 libjpeg 的功能阉割版,压缩图片默认使用的是 standard huffman,而不是 optimized huffman,也就是说使用的是默认的哈夫曼表,并没有根据实际图片去计算相对应的哈夫曼表,Google 在初期考虑到手机的性能瓶颈,计算图片权重这个阶段非常占用 CPU 资源的同时也非常耗时,因为此时需要计算图片所有像素 argb 的权重,这也是 Android 的图片压缩率对比 iOS 来说差了一些的原因之一。

四、图像压缩与 Huffman 算法

这里简单介绍一下哈夫曼算法,哈夫曼算法是在多媒体处理里常用的算法之一。比如一个文件中可能会出现五个值 a,b,c,d,e,它们用二进制表达是:

a. 1010
b. 1011
c. 1100
d. 1101
e. 1110

我们可以看到,最前面的一位数字是 1,其实是浪费掉了,在定长算法下最优的表达式为:

a. 010
b. 011
c. 100
d. 101
e. 110

这样我们就能做到节省一位的损耗,那哈夫曼算法比起定长算法改进的地方在哪里呢?在哈夫曼算法中我们可以给信息赋予权重,即为信息加权,假设 a 占据了 60%,b 占据了 20%, c 占据了 20%,d,e 都是 0%:

a:010 (60%)
b:011 (20%)
c:100 (20%)
d:101 (0%)
e:110 (0%)

在这种情况下,我们可以使用哈夫曼树算法再次优化为:

a:1
b:01
c:00

所以思路当然就是出现频率高的字母使用短码,对出现频率低的使用长码,不出现的直接就去掉,最后 abcde 的哈夫曼编码就对应:1 01 00
通过权重对应生成的的哈夫曼表为:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

定长编码下的abcde:010 011 100 101 110,使用哈夫曼树加权后的编码则为 1 01 00,这就是哈夫曼算法的整体思路(关于算法的详细介绍可以去查阅相关资料)。

所以这个算法一个很重要的思路是必须知道每一个元素出现的权重,如果我们能够知道每一个元素的权重,那么就能够根据权重动态生成一个最优的哈夫曼表。

但是怎么去获取每一个元素,对于图片就是每一个像素中 argb 的权重呢,只能去循环整个图片的像素信息,这无疑是非常消耗性能的,所以早期 android 就使用了默认的哈夫曼表进行图片压缩。

五、libjpeg 与 optimize_coding

libjpeg 在压缩图像时,有一个参数叫 optimize_coding,关于这个参数,libjpeg.doc 有如下解释:

TRUE causes the compressor to compute optimal Huffman coding tables
for the image. This requires an extra pass over the data and
therefore costs a good deal of space and time. The default is
FALSE, which tells the compressor to use the supplied or default
Huffman tables. In most cases optimal tables save only a few percent
of file size compared to the default tables. Note that when this is
TRUE, you need not supply Huffman tables at all, and any you do
supply will be overwritten.

由上可知,如果设置 optimize_coding 为 TRUE,将会使得压缩图像过程中,会先基于图像数据计算哈弗曼表。由于这个计算会显著消耗空间和时间,默认值被设置为 FALSE。

那么 optimize_coding 参数的影响究竟会有多大呢?查阅一些博客资料介绍,使用相同的原始图片,分别设置 optimize_coding=TRUE 和 FALSE 进行压缩,发现 FALSE 时的图片大小大约是 TRUE 时的 5-10 倍。换言之就是相同文件体积的图片,不使用哈夫曼编码图片质量会比使用哈夫曼低 5-10 倍。

关于这个差异我们再去查阅其他资料,发现有两篇讨论非常热烈:Investigate using “optimize_coding” when encoding to JPEG,About libjpeg optimize_coding,甚至Skia 的官方人员也参与了讨论,他据此测试了两组数据:

sample image 1 (RGB gradients):
default (80): 2.5x slower, 34% smaller
quality 0: 1.7x slower, 52% smaller
quality 20: 2.1x slower, 55% smaller
quality 40: 2.3x slower, 37% smaller
quality 60: 2.5x slower, 36% smaller
quality 100: 3.9x slower, 22% smaller

sample image 2 (photo):
default (80): 2x slower, 8% smaller
quality 0: 1.5x slower, 49% smaller
quality 20: 1.7x slower, 22% smaller
quality 40: 1.9x slower, 15% smaller
quality 60: 1.9x slower, 11% smaller
quality 100: 2x slower, 9% smaller

可以看到效果并不是 5-10 倍的体积差距,最多也就在 2 倍而已,有国人也测试了一下,结果一致:JPEG Optimized Huffman。

尽管如此,社区里对此的疑虑并没有彻底打消,最终,官方人员修改了这个默认的实现:skia / skia.git / 0a35620a16b368356888d15771392fb00cbb777d(skia.googlesource.com/skia.git/+/… ) 。在 SkImageDecoder_libjpeg.cpp 文件中给 optimize_code 赋值了一个默认值 TRUE。

六、Android 与 optimize_coding

那么在 Android 中有没有使用哈夫曼变长编码呢?查阅了 7.0 源码,如下:

/* Use Huffman coding, not arithmetic coding, by default */
cinfo->arith_code = FALSE;

可以看到注释里面很清楚,默认是哈夫曼变长编码,而不是算数编码。同时去查阅 14 年时的 Android 4.4 源码,发现依旧如此。

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级安卓工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Android移动开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
img

文末

我总结了一些Android核心知识点,以及一些最新的大厂面试题、知识脑图和视频资料解析。

需要的小伙伴私信【学习】我免费分享给你,以后的路也希望我们能一起走下去。(谢谢大家一直以来的支持,需要的自己领取)

直接点击链接也可以领取哦!

Android学习PDF+架构视频+面试文档+源码笔记

部分资料一览:

  • 330页PDF Android学习核心笔记(内含8大板块)

  • Android学习的系统对应视频

  • Android进阶的系统对应学习资料

  • Android BAT大厂面试题(有解析)

]

[外链图片转存中…(img-4hOpzD3v-1711294556036)]

  • Android学习的系统对应视频

  • Android进阶的系统对应学习资料

[外链图片转存中…(img-pL1SRqPt-1711294556037)]

  • Android BAT大厂面试题(有解析)

  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值