背景与现状

随着版本的迭代,业务的增加,QQ音乐apk的大小已经超过25M,其中res目录占用的大小超过5.5M,所以提出了对安装包进行瘦身的技术需求。业务的增加导致图片越来越多,通过分析可以知道PNG格式图片是项目中数量最多的图片,关于PNG图片的介绍可以参考:PNG文件格式详解。为了实现减包任务,对图片进行压缩是很重要的一部分。

为了实现PNG图片的压缩,之前的处理方式是先在本地进行压缩,然后提交到SVN,再打包发布。一般采用在线压缩工具处理,将res目录下的PNG图片批量手动处理,这种方式容易出现的问题是:

1) 为了追求高的压缩率,容易出现一张图片重复压缩的情况,导致图片严重失真;

2) 不能自定义参数开发,无法满足开发需求;

3) 压缩效率比较低,每次发布时都需要人为进行一次图片的压缩。

压缩工具及原理分析

tinypng

1)原理介绍

根据官网https://tinypng.com/介绍,主要是使用Quantization的技术,通过合并图片中相似的颜色,通过将 24 位的 PNG 图片压缩成小得多的 8 位色值的图片,并且去掉了图片中不必要的 metadata(元数据,从 Photoshop 等工具中导出的图片都会带有此类信息),这种方式几乎能完美支持原图片的透明度。有部分文档指出tinypng同时采用了pngquant、optipng、advpng几种脚本。图片的压缩率能达到50%以上。

2)在线API

提供在线API供开发者二次开发,支持Ruby、PHP、Node.js、Python、Java等语言,其中Java库源码地址为tinify-java。但是,在线api需要申请api-key,并且对调用次数有限制,可以免费调用500次。在公司内部的开发网环境无法正常运行,无法通过API在线调用接口。

pngquant

根据官网https://pngquant.org/介绍,pngquant是国外的一个有损的PNG压缩开源库,提供了命令行形式和源码库形式。将24位或32位的RGBA PNG图转换成8位PNG图并保留透明度通道。通过这个库的转化可以显著减少png文件大小(通常减少70%)。生成的图片文件可以兼容所有现代web浏览器,在IE6下比24-bit PNGs也有更好的表现。它的特性有:

1)结合Vector_quantization算法生成高质量的色彩范围;

2)独特的自适应抖动算法,比标准的FloydSteinberg算法具有更强的抗噪性;

3)易与集成,提供了shell脚本,图形化界面,服务端库,PS插件;

4)具有快速模式,用于处理大批图片。源码库地址为pngquant。

其他PNG压缩工具

网上流行的PNG压缩工具主要有ImageAlpha、ImageOptim、pngcrush、optipng、pngout、pngnq、advpng等。他们的对比可以参考以下文章;

1)John Wong的PNG图片极限压缩,介绍了ImageAlpha和ImageOptim两种压缩方法;

2)图片优化的那些工具,介绍了pngcrush、optipng、pngout、pngnq等。

各压缩工具的对比表格如下:


根据资料显示,tinypng、pngquant、ImageAlpha、pngnq都是有损压缩,基本采用的都是quantization算法,将24位的PNG图片转换为8位的PNG图片,减少图片的颜色数;pngcrush、optipng、pngout、advpng都是无损压缩,采用的都是基于LZ/Huffman的DEFLATE算法,减少图片IDAT chunk区域的数据。一般有损压缩的压缩率会大大高于无损压缩。

压缩对比

一些流行的PNG压缩工具的压缩率对比可以参照:常用PNG压缩工具压缩率对比。在参考以上文章的基础上,本文主要针对pngquant和tinypng做出了对比。

1.单个图片压缩对比

选取QQ音乐Android项目中占用空间最大的几个PNG图片进行压缩效果的对比,通过pngquant.exe脚本以及tinypng网站分别进行单个压缩,压缩率如下图所示:(pngquant使用默认压缩品质)

从表格中可以知道,tinypng的压缩率大概比pngquant的压缩率会高10%左右。其中pngquant压缩过程会出现比原来图片大的情况,所以在实际利用脚本压缩过程中需要对压缩后的图片和原来图片大小进行对比,如果出现变大的情况应该舍弃。

2.批量图片压缩对比

由于项目中存在大量的PNG格式图片,所以不可能单个单个进行压缩,需要通过批量压缩来进行,tinypng的批量压缩目前只能是在官网进行(由于在线API的开发会有图片数量受限以及开发网环境受限等),而pngquant的批量压缩可以通过自己完全自定义开发,调用压缩脚本进行压缩,在官网上可以下载windows和linux版本下的运行文件。

选取项目中大小4KB以上的106个PNG图片进行压缩实现,效果对比如下:(pngquant使用默认压缩品质)

从表格中可以知道,tinypng工具由于是采用网站在线压缩的方式,所以批量上传过程中容易出现上传不成功等错误,而pngquant采用的是本地脚本压缩,所以这个问题可以有效避免。由于tinypng采用了多种压缩算法,压缩效果会好于pngquant,大概可以多压缩12%左右,不过由于是有损压缩,所以在追求尽可能增加压缩比率的同时也应该考虑压缩后图片的显示效果。

3.本地运行脚本对drawable目录压缩

使用pngquant脚本压缩资源目录res下的drawable、drawable-hdpi、drawable-ldpi、drawable-mdpi、drawable-xhdpi、drawable-xhdpi-v21、drawable-xxhdpi、drawable-xxxhpi等8个文件夹,在多线程的情况下,共耗时17s940ms。主线48322版本的资源目录采用各种压缩品质(通过设置—quality参数实现)进行压缩,对比效果如下图,综合考虑压缩率和压缩后效果,最终采用品质为90的压缩方案,可以减包1.97MB。

4.结论

在综合比较tinypng和pngquant的基础上,项目最终考虑使用pngquant来对PNG图片进行批量压缩,主要考虑有:

1)虽然在pngquant采用默认压缩品质的情况下压缩率会低于tinypng,但是tinypng是在线压缩工具,不好自定义控制与维护,tinypng压缩后的显示效果还有待进一步验证;

2)同时,pngquant脚本可以自定义压缩品质,采用压缩品质更低(比如90)的情况下,压缩率会高于tinypng,而且pngquant是开源的,容易维护,风险可控。

使用pngquant的压缩流程

1.gradle编译

项目PNG图片压缩类存放在工程目录buildSrc下,是使用groovy开发的ImageCompressionTask.groovy(由haodongyuan开发),在build.gradle或build_server.gradle中通过以下方式引用:(手动运行compressImages任务即可实现压缩处理,其中quality表示压缩品质,compress表示是否开启压缩)

  1. import com.tencent.qqmusic.ImageCompressionTask

  2. task compressImages(type: ImageCompressionTask) {

  3. multiThread = true

  4. verbose = false

  5. backupRoot = new File('res-backup')

  6. sourceRoot = rootDir

  7. quality = 90

  8. compress = true

  9. folders = [

  10.   "res/drawable",

  11.   "res/drawable-hdpi",

  12.   "res/drawable-ldpi",

  13.   "res/drawable-mdpi",

  14.   "res/drawable-xhdpi",

  15.   "res/drawable-xhdpi-v21",

  16.   "res/drawable-xxhdpi",

  17.   "res/drawable-xxxhdpi"

  18. ]

  19. }

2.RDM自动化编译

如果要做成gradle自动化编译,除了在gradle中加入上面代码外,还需要使用下面语句做成任务依赖,需要在编译apk之前执行压缩任务:

copyNativeLibs.dependsOn compressImages

并且同时关闭Android编译工具AAPT自带的压缩效果:

  1. aaptOptions {

  2.    cruncherEnabled = false

  3. }

pngquant的Linux版本的运行要依赖一些是so文件,需要在build.sh文件中加入依赖的so库:

  1. #加入动态加载pngquant压缩的依赖so库

  2. export LD_LIBRARY_PATH=$(cd `dirname $0`; pwd)/scripts/Tools/pngquanti/linux/lib

  3. ln -s $(cd `dirname $0`; pwd)/scripts/Tools/pngquanti/linux/lib/liblcms2.so.2.0.8 $(cd `dirname $0`; pwd)/scripts/Tools/pngquanti/linux/lib/liblcms2.so.2

  4. ln -s $(cd `dirname $0`; pwd)/scripts/Tools/pngquanti/linux/lib/ld-2.23.so $(cd `dirname $0`; pwd)/scripts/Tools/pngquanti/linux/lib/ld-linux-x86-64.so.2

  5. ln -s $(cd `dirname $0`; pwd)/scripts/Tools/pngquanti/linux/lib/libpng15.so.15.27.0 $(cd `dirname $0`; pwd)/scripts/Tools/pngquanti/linux/lib/libpng15.so.15

自动化编译过程压缩脚本运行的流程为:


从压缩流程图中可以知道,在项目资源目录下PNG图片数据过多时,默认应该采用多线程执行压缩脚本,为了避免出现重复压缩的情况,在进行压缩之前需要读取图片的压缩信息,压缩过的不再压缩,同时,压缩完成后,需要对压缩处理过的图片写入压缩信息,方便下一次读取。由于pngquant脚本的特殊性,需要判断压缩后文件是否大于原始文件,大于则需要删除并记录。

3.优势

使用pngquant自动压缩的优势主要有三点:

1)选择pngquant做为png图片压缩脚本,可以在不影响图片显示效果的基础上最大化压缩(采用压缩品质90);

2)同时通过groovy脚本对图片进行批量压缩处理与加入额外压缩信息,可以提高压缩效率并防止重复压缩;

3)做成RDM自动化压缩后,可以有效减少开发人员的工作量,方便后期维护。

JPG的压缩

JPG的压缩途经有,公司内部的优图工具:优图JPG压缩;在线jpg压缩工具tinyjpg:https://tinyjpg.com/;脚本工具jpegtran:http://jpegclub.org/jpegtran/等。由于优图工具是无损压缩,有专业的优图团队进行维护,并且压缩率可以达到20%以上,而tinyjpg是有损压缩,会出现重复压缩的情况且不方便维护,所以项目优先考虑采用优图工具进行压缩。

选取工程res目录下的49个JPG文件进行压缩对比,可以减少0.23MB,效果如下:

第三方jar包中的图片压缩

通过观察apk包会发现,assets目录下会存在一些新生成的图片目录,包括common、drawable、images、Recommend目录,通过分析发现,这些png图片来自项目引用的第三方jar包,其中TVK_MediaPlayer-V3.6.0.10721.jar会产生common、Recommend目录,videoad-sdk-1.7.2-20160527.jar会产生images目录,weiboSDKCore_3.1.2.jar会产生drawable相关目录。可以知道,这部分图片的优化空间有限,重点需要对TVK_MediaPlayer、videoad-sdk、weiboSDKCore三个jar包手动进行PNG图片压缩。总共可以减少41KB。

部分jar包PNG图片压缩减少的大小对比:

总结

本次工程图片压缩过程,主要学习了PNG图片的主要压缩脚本(tinypng/pngquant/pngout)以及JPG图片的压缩工具(优图/tinyjpg),经过对比最终选择pngquant与优图作为工程PNG和JPG图片的压缩工具。为了实现PNG图片压缩的自动化管理,将pngquant脚本集成到RDM编译,主要遇到的问题有:

1)groovy脚本执行Linux命令,Linux环境下运行bin文件,需要首先使用chmod赋予权限;

2)gradle的编译顺序问题,根据编译过程的依赖链关系,需要将compressImages任务放到最开始;

3)pngquant的运行需要依赖一些so库,所以需要通过重设“LD_LIBRARY_PATH”来定向加载so库,同时,通过ln命令来设置so库的软连接关系;

4)路径识别问题,一开始总是无法找到图片路径,后面发现是在压缩脚本路径引用的时候多加了双引号(“”),导致在RDM平台无法识别;

5)RDM自动化编译时间增加的问题,由于res目录下的JPG图片和第三方jar包的PNG图片都是本地手动压缩处理的,不用考虑时间问题。res目录下的PNG图片是在RDM平台自动压缩处理的,要考虑时间成本,如下图所示,加上压缩脚本,大概会多耗时26秒左右。

通过本次图片压缩优化,可以达到以下几个目的:

1)res目录下的PNG可以减少1.97MB,PNG的减包效果如下图,res目录下的JPG图片可以减少200KB,第三方jar包(assets)目录的PNG图片大小可以减少40KB。总共可以减包2.22MB;

PNG压缩前:

PNG压缩后:

2)实现PNG图片自动化压缩处理,大大提高了开发效率,方便维护管理;

3)通过groovy脚本可以实现自定义压缩品质与写入压缩信息,有效控制压缩率和智能判断,避免二次压缩的出现。

参考

[1]http://blog.csdn.net/bisword/article/details/2777121

[2]https://tinypng.com/

[3]https://pngquant.org/

[4]http://www.jianshu.com/p/a721fbaa62ab

[5]http://johnwong.github.io/showcase/2015/02/19/png-compress.html

[6]http://ued.ctrip.com/blog/image-optimization-tools.html

[7]http://advsys.net/ken/utils.htm

[8]http://jamiemason.github.io/ImageOptim-CLI/comparison/png/photoshop/desc/

[9]https://my.oschina.net/shede333/blog/373780

[10]https://tinyjpg.com/

[11]https://www.oschina.net/translate/4-free-tools-to-optimize-and-compress-png-images-without-loosing-quality

各种 PNG图片压缩对比分析相关推荐

  1. 可能是最详细的Android图片压缩原理分析(二)—— 鲁班压缩算法解析

    本篇文章已授权微信公众号guolin_blog(郭霖)独家发布 稀土掘金链接 前言 通过上一篇,我们了解了一些关于图片压缩的基础知识,这篇文章我们主要讲解一下鲁班压缩的算法逻辑,很多博客都是从Gith ...

  2. 可能是最详细的Android图片压缩原理分析(一)—— Android图片压缩必备基础知识

    本篇文章已授权微信公众号guolin_blog(郭霖)独家发布 稀土掘金链接 前言: 最近在研究图片压缩原理,看了大量资料,从上层尺寸压缩.质量压缩原理到下层的哈夫曼压缩,走成华大道,然后去二仙桥,全 ...

  3. android 图片压缩方法分析

    demo 参考会放在末尾 1 质量压缩 先看截图吧,手机截图可能有点不优雅 通过压缩前后数据可以知道, 质量压缩之后不会减少图片的像素,它是在保持像素的前提下改变图片的位深及透明度,来达到压缩图片的目 ...

  4. Web端CAD图形找不同?一键在Web端找出CAD图不同并对比分析

    引言 在实际中,当多专业设计协助时,遇到图纸更新后,要对比图纸找出图纸的不同处,一直是一个比较耗时费力的事情,也是业内的一大痛点.一般CAD新旧图纸的内容对比,包括增加新的图形元素.减少原有的图形元素 ...

  5. Android中图片压缩分析(上)

    此文章首发:https://mp.weixin.qq.com/s/QZ-XTsO7WnNvpnbr3DWQmg 一.前言 在 Android 中进行图片压缩是非常常见的开发场景,主要的压缩方法有两种: ...

  6. 图片上传压缩android,android 图片上传压缩常见问题分析

    图片的上传与压缩是android经常需要用到的步骤,那么,如何解决上传图片oom问题呢?android 图片上传压缩常见问题分析,希望可以帮助大家更加的了解android 图片方面的困惑. 下面,是我 ...

  7. JPEG/Exif/TIFF格式解读(1):JEPG图片压缩与存储原理分析

    JPEG文件简介 JPEG的全称是JointPhotographicExpertsGroup(联合图像专家小组),它是一种常用的图像存储格式, jpg/jpeg是24位的图像文件格式,也是一种高效率的 ...

  8. Android图片系列-2.Android App图片压缩、裁剪分析整理

    移动端常用的图片格式有PNG和JPEG,目前ios手机和大部分安卓手机拍照生成的图片默认格式都是JPEG.我们开发APP的时候通常使用的是PNG,这可能是考虑到图片质量效果.PNG图片是无损压缩格式, ...

  9. 图片加载框架对比分析

    glide Google推荐的图片加载库,专注于流畅的滚动. 1.优点  1)使用RGB_565,内存占用比Picasso小一半.  2)图片展示和页面的生命周期一致(对context有类型要求)  ...

最新文章

  1. GPU上稀疏矩阵的基本线性代数
  2. Python超简单容易上手的画图工具库
  3. iptables限制最大连接数_性能调优,让你的服务器更强大!增加TCP连接最大限制...
  4. leetcode 521. 最长特殊序列 Ⅰ(Java)
  5. 一行命令 优化上传速度
  6. 【招聘(广州)】 招聘.NET程序员
  7. mysql 6.5安装配置,RedHat6.5安装MySQL5.7教程详解
  8. blog error #include stream.h
  9. 解决Python中sum函数出现的TypeError: unsupported operand type(s) for +: 'int' and 'list'错误问题
  10. del服务器如何收系统日志,利用Rsyslog集中收集系统日志和用户操作记录以及相关处理方法...
  11. Android开发工具Android Studio、Android SDK和Genymotion完全配置
  12. honeywell新风系统控制面板说明_详解装了新风系统,怎么清洗维护简单?
  13. 匿名函数与内置函数(python3入门)
  14. Java进程与子进程交互
  15. HDU2000 ASCII码排序【字符串排序】
  16. python错误:ImportError: DLL load failed: 找不到指定的程序,from PyQt6 import QtCore, QtGui, QtWidgets
  17. webrtc系列3——对于stun和turn的理解
  18. Unity之FBX文件操作学习笔记(二)SDK sample代码初探
  19. 谢耳朵获艾美奖最佳男主角 三夺视帝感谢男友
  20. JAVA工程师个人简历中的项目经验范文

热门文章

  1. 计算机毕业设计Java网上租房管理(源码+系统+mysql数据库+Lw文档)
  2. 基于随机森林、svm、CNN机器学习的风控欺诈识别模型
  3. burpsuit 抓取https包,证书问题,此证书已在此前安装为一个证书权威机构
  4. ┎结构之美┒之Trie树
  5. 并行计算综述————第一章 并行计算硬件平台:并行计算机
  6. chatgpt国内能用吗?详细解读gpt的使用方法
  7. stm8下载程序(使用ST-LINK下载器和STVP下载软件)
  8. GeenMedical:文献查询、筛选、引用排序、相似文献、全文下载、杂志分区、影响因子、结果导出、杂志评述、直接投稿,一站服务
  9. 文件重命名不了怎么办,教你解决办法
  10. python 操作ps脚本_python – 有没有办法以编程方式获得ps输出?