一篇文章显得略长,本文对应原书11-15章。序言、前言、第1-5章,请参考Part 1,第6-10章,请参考Part 2。

评价数据压缩

并不是每个算法都适用于所有的使用场景。

使用场景

线下压缩,客户端解压

两个场景:

  • 打包的应用程序或电子游戏,相应的资源文件中常常包含大量的图片、视频和音乐;
  • 艺术家创作并共享他们的作品。

压缩的目的是使多媒体文件尽可能地小。

权衡取舍:多媒体文件的品质。

客户端压缩,云端解压

先使用二进制序列化格式将这组数据序列化,然后在发送到服务器之前再用GZIP对它进行压缩。

权衡取舍:对于移动设备,需要消耗电池的电量去压缩数据。

云端压缩,客户端解压

放到云端压缩的数据大致可分为两类:

  • 由云端资源生成的动态数据
  • 为提高计算效率而传输到云端的大量数据

客户端压缩,客户端解压

场景:客户端应用程序之间相互通信,相互发送对等网络包、图片或GPS信息。

更多的是需要权衡设备的功能、压缩和解压缩时间以及需要数据的迫切性。

需求

做选择前的调研工作:

  • 了解要处理的数据:数据的类型,内部结构,使用方式;
  • 了解算法的各项指标;
  • 了解在给定的情况下的需求。

压缩率

压缩率,内容压缩后大小与压缩前大小之比,最重要指标。

例外:关注的主要是压缩后的文件大小时,ZPAQ是很不错,但它不适用于在移动设备。

压缩性能

压缩性能,指将数据转换为压缩后的形式需要多长时间。通常有两个评价指标:CPU速度和内存。

大多数客户端(尤其是移动设备)内置对硬件压缩编解码器的支持,至少是对某些有损压缩类型的数据。像JPG和H.264这样的文件可以很容易传输到这些硬件上,而服务器端通常没有配置这些硬件。在客户端进行压缩要比在服务器端更容易。

解压性能

解压性能,指从压缩后数据解压缩得到原始数据需要多长时间。重点关注性能时,解压速度的重要性超过其他所有指标。

ZPAQ是一种非常高效的压缩算法,使用神经网络作为编解码器,解压时需要运行大量的资源。使得它注定与只拥有较少的CPU和电池资源的移动设备无缘。

最初几个版本的WebP相比JPG来说,所需的空间更小,且图片质量更高,美中不足的是解码所需的时间几乎是JPG的两倍。于是不少浏览器公司持观望态度。

在台式和移动设备中普遍存在的硬件解码器,正在改善这种情况。处理JPG、OGG和H.264的硬件芯片,正在提高专门为它们而设计的算法的解码性能,让它们成为某些情况下更好的选择。

解码流能力

通常认为解压算法要处理完整数据包,即解码前所有的数据都必须在内存中。事实上并不是如此。

一些通用压缩算法如GZIP、BZIP2,大多数多媒体压缩算法像H.264能在流模式下工作。数据以分块形式发送到客户端,一到客户端就开始解码(即分块解码)。

比较

说到比较,自然得提基准测试,Benchmark Testing。压缩算法的基准测试主要有3类:

  • 大文本压缩:Large TextCompression Benchmark,LTCB,用于1G大文本文件;
  • Squash压缩:XML、文本、图像以及其他数据格式;
  • Squeeze Chart压缩:各种文本、音频以及位图。

Weissman Score:魏斯曼评分,魏斯曼教授设计的一种度量数据压缩性能的方法,用数据集的压缩率除以其编码速度作为衡量标准。

计算公式:《数据压缩入门》笔记-Part 3_客户端
其中,《数据压缩入门》笔记-Part 3_客户端_02表示压缩率,《数据压缩入门》笔记-Part 3_客户端_03表示压缩时间,加上划线的表示作为衡量标准的压缩算法,《数据压缩入门》笔记-Part 3_数据_04是常量。

压缩图像数据

理解图像质量与文件大小

图像压缩工具会提供一个整数参数,让你来决定图像的质量。

每张照片的最佳平衡点都可能不同。一个价值百万美元的问题:在规模庞大的情况下,怎样才能找出每幅图像的最佳质量值?

imgmin:开源项目,指出:对于级别在75~100的JPG压缩,通常用户只能感受到很小的质量差别。大多数的大型网站往往将所有JPG图片的质量值设定在75左右。

什么降低图像质量

在看图像时,人类的眼睛对很多事物很敏感,其中包括边缘和渐变。

banding:色彩断层,由于颜色的缺失导致渐变图像的平滑质量降低的现象。

量化(quantization)和区块化(blocking)是导致图像压缩出现视觉问题的最常见的两种形式。大多数图像压缩算法将图像数据先切分为像素块,然后再量化,以减少图像中不同的颜色数量,再在此基础上基于图像的局部性进行修改。

在一幅真正随机的图像中,两个相邻的像素之间并不会存在相关性;而在一张照片中,两个相邻的像素之间往往是渐变的,颜色相似。即局部区域关联性。因此,JPG会将图像的像素切分为8×8的小块,然后试着找出与此区域相似的颜色;但是,相邻区块之间的颜色可能不太相同,区块之间的边缘会变得很明显很突兀。

视觉伪影(visual artifacts)。

度量图像质量

人脑对图像数据的质量的判断是偏感性的,非量化的,且对于细微的区别无法区分。

因此,需要数学上的、可度量的、能用编程实现的图像质量评价概念(指标)。当前常用的,相互之间有些竞争的两个指标是:

  • PSNR:Peak Signal-to-Noise Ratio,峰值信噪比
  • SSIM:Structural Similarity Index Measure,结构相似性

PSNR通常表示一个信号的最大可能功率与影响它的表示精度的破坏性噪声功率的比值(以对数分贝为单位)。这一度量的基础是压缩图片的均方误差(mean-square error,MSE),即原始图像的值与压缩后的值差别有多大。

PSNR与MSE之间,存在着反比关系。当误差的数量较少时,图片的质量就比较高(PSNR同样如此),反之亦然。需要注意的是,如果你试着去计算两张一样的图像的均方误差值,其结果会为0,那么对应的PSNR会是未定义的(除以0)。

PSNR存在的一些问题:

  • 计算的是去噪之后经过均方处理的误差,所以它稍微有些偏向过度平滑(即模糊)的结果。用通俗的话来说,即使将图像的部分结构移除,得分还是比较高(忽略图像缺失的那部分)。因此,PSNR并不总是与源图像或者应用到其上的效果类型相一致。
  • PSNR严格依赖于数值比较,没有考虑任何与人类视觉系统相关的生物因素。因此会出现从数值上去看很好,但用眼睛去看就有比较明显的图像质量问题的情况。

SSIM,比较图像的压缩质量时考虑人眼的感知情况。通过比较源图像与压缩后图像的边缘相似性来实现的。SSIM使用的两张图像中,一张为未经压缩的无失真图像,另一张为失真后的图像。

给定两个图像《数据压缩入门》笔记-Part 3_客户端_05《数据压缩入门》笔记-Part 3_压缩算法_06,SSIM的计算公式《数据压缩入门》笔记-Part 3_客户端_07

其中《数据压缩入门》笔记-Part 3_客户端_08《数据压缩入门》笔记-Part 3_客户端_05的平均值,《数据压缩入门》笔记-Part 3_压缩算法_10《数据压缩入门》笔记-Part 3_压缩算法_06的平均值,《数据压缩入门》笔记-Part 3_数据_12《数据压缩入门》笔记-Part 3_客户端_05的方差,《数据压缩入门》笔记-Part 3_数据_14《数据压缩入门》笔记-Part 3_压缩算法_06的方差,《数据压缩入门》笔记-Part 3_客户端_16《数据压缩入门》笔记-Part 3_客户端_05《数据压缩入门》笔记-Part 3_压缩算法_06的协方差。《数据压缩入门》笔记-Part 3_数据_19《数据压缩入门》笔记-Part 3_数据_20是用来维持稳定的常数,《数据压缩入门》笔记-Part 3_数据_21是像素值的动态范围。《数据压缩入门》笔记-Part 3_笔记_22《数据压缩入门》笔记-Part 3_压缩算法_23

SSIM取值范围为[0,1],取值为1表示的是压缩后的图像与源图像完全相同,取值为0表示压缩后的图像与源图像完全不同。

让想法真正工作

为了让用户感受到相同的视觉品质,不同类型的图像需要有不同的输出设定。

图像尺寸

更好的方法是直接在云端调整图像的大小,或者在某处缓存调整大小后的图像,这样就能向小屏幕发送小尺寸的图像。

对用户而言,合适的图像尺寸好处多多:

  • 发送的数据量更少更快,也会更节省用户的套餐费用;
  • 节省用户的设备空间;
  • 无须再调整图像的大小;
  • 解码会更快,加载会更快,显示也会更快…

图像格式

最常用的4种图像格式:PNG、JPG、GIF和WebP。

PNG

PNG:Portable Network Graphics format,便携式网络图像格式,使用GZIP这样的压缩工具使数据量变小。无损压缩,压缩后的图像质量与源图像相同。

PNG的优点:

  • 既能保证图像的高质量又能压缩数据量,当然压缩程度没有有损压缩那么大;
  • 对透明度的支持。除红、绿和蓝3种颜色通道外,还支持alpha通道,可以定义渲染时哪些像素是透明混合的;
  • 允许文件中存在元数据块。使得图像编辑器(以及生成图像的客户端设备)可以将额外的数据附加到文件中。

缺点:文件偏大。

解决方法:使用有损PNG压缩工具。这类工具非常多,Google搜索即可得知。

JPG

JPG,也叫JPEG,Joint Photographic Experts Group,联合图像专家小组。

一种用于摄影图像的格式,不支持alpha透明度。包含一个功能强大的有损压缩工具,可以通过一个质量值来控制它,以达成对文件大小与图像质量的权衡取舍。

JPG这种压缩格式的基础是分块编码。一幅图像会被分成8×8的小块,然后在每块上应用各种不同的变换,再将变换之后的小块组合起来交给统计编码算法处理。

JPG算法的工作原理图:

《数据压缩入门》笔记-Part 3_笔记_24


分块过程只对摄影图像(即照片)适用。如果你想压缩的是颜色比较少的图像,如手绘卡通画,PNG对应的无损压缩工具表现得会更好,因为它可以将很长的类似颜色值压缩为一个记号。

JPG格式的好处:

  • 与PNG类似,JPG文件中也包含元数据块,这意味着图像编辑器或照相机都能在文件中插入一些不需要的数据;
  • 大多数移动设备现在有系统可用的JPG编码和解码的硬件,意味着解码一个JPG文件需要的时间要比解码同样大小的PNG文件短很多。

GIF

Graphics Interchange Format,图像互换格式,一种位图图形文件格式,以8位色(即256种颜色)重现真彩色的图像。支持透明度、动画。

GIF格式文件的生成包含两个压缩步骤:

  • 有损的色彩数量压缩,将整个图像的颜色数量减少到只有256种;
  • 无损LZW压缩。

将图像颜色的数量减少到256种会极大地降低图像的质量,而好处则是文件压缩得更小,这也会使LZW算法的压缩效果更好。网站对GIF有很好的支持,但在本地平台上没有得到统一支持(比如???)。

cats on the internet thing:说到GIF动图,你们可能会想到各种各样关于猫的图片,聊天工具上现在也有很多关于猫的表情包。GIF让猫咪们统治了互联网。如这篇

WebP

WebP格式提供介于PNG和JPG之间的中间地带。WebP既支持无损模式和透明度,同时也支持有损模式。

存在的不足:

  • 浏览器不是100%支持;
  • 开发移动应用程序时,为了使用它还需要包含相应的库(安卓平台除外,要知道Android是Google自家开发,当然会支持WebP);
  • 在有损压缩模式下的高压缩率,意味着解压时要比JPG或PNG格式慢一些。

选择

如何选择:

《数据压缩入门》笔记-Part 3_数据_25


专利权(及其保护法)真的是阻碍技术发展,限制大众过上更好生活的魔鬼。

1985年,Unisys公司申请LZW算法的专利。后来,CompuServe公司以LZW算法为基础开发89a格式(即后来的GIF格式)。1993年Netscape浏览器增加对IMG HTML标签、89a格式的支持。动画图像在互联网上流行起来。Unisys公司眼红,申请对其专利的保护。上法庭。对使用89a格式的所有软件收取专利使用费。几个月后,PNG格式诞生。

商业也是。

2013年,谷歌和开源贡献者开发出WebP,谷歌自家的Chrome当然支持WebP。但Mozilla的FireFox浏览器拒绝支持此格式,甚至开源MozJPEG编解码算法来改善JPG的无损压缩预处理步骤。不过没坚持两年。

GPU纹理格式

计算机不能直接利用压缩格式的数据绘制图像,而是需要先将压缩的数据加载到内存中,然后再解压为系统可直接渲染的格式。默认情况下,图像会被解压为每像素32位的格式,其中红绿蓝三种颜色通道以及alpha通道都是8位(即RGBA_8888这种表现形式)。然后,图像会被当作纹理传输到GPU中,即生成的每一个位图都会同时需要CPU和GPU内存。结果就是,不论图像在网络上的压缩质量如何,当在设备上显示时,它就会占用大量的内存。

好消息:存在GPU能直接渲染的有损像素压缩格式,如:DXT、ETC和PVR。

视频游戏开发者得知道这一点,将从网络中传输过来的数据解压为这些压缩格式之一,这样GPU就可直接渲染,而无须解压。

矢量格式

光栅格式图像:Raster Format Image,图像通过二维网格中的像素来显示,这些像素表示的是图像本身的颜色。从远处观察图像时,像素之间的边缘就会消失,人眼(被欺骗)看到的就是平滑的渐变颜色。

矢量图像格式:不传输图像,而传输图像的生成语句。

缺点:

  • 包含的像素细节少,不适合用来生成高质量的图像;
  • 客户端时间开销。在客户端渲染时,需通过执行指令来为GPU生成光栅化的图像。

优点:

  • 简单,传输数据小;
  • 主要由线组成的技术图纸,列出其中的点并描述如何连接这些点,比传输每个像素有效得多;
  • 能精确地放大缩小,常用于在不同的设备上显示同一图像的缩略图、图标和全屏图。

SVG是一种常用的矢量图像格式。无论源数据多大,使用SVG,能用很少的内存来描述图像,并在客户端生成高质量的与分辨率无关的图像。局限,只能用来表示某种类型的比较简单的图像。

SVG文件,实际上就是XML文件,需要遵从一些规则,否则客户端将渲染失败。

如下图,IDEA自带SVG文件渲染能力,对于格式有问题的文件的报错:

《数据压缩入门》笔记-Part 3_压缩算法_26


示例:

<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg"  x="0px" y="0px"
	 width="512px" height="512px" viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
<g>
	<path d="M429.9,95.6c-40.4-42.1-106-42.1-146.4,0L256,124.1l-27.5-28.6c-40.5-42.1-106-42.1-146.4,0c-45.5,47.3-45.5,124.1,0,171.4
		L256,448l173.9-181C475.4,219.7,475.4,142.9,429.9,95.6z"/>
</g>
</svg>

渲染效果:

《数据压缩入门》笔记-Part 3_压缩算法_27


总结:矢量格式适用于标志、技术图纸以及简单的图像样式,光栅格式则适用于相片及其他信息密集的图像。

捷径

现代应用程序,图像占比很大;如果想减少应用程序的数据占用空间,首要考虑压缩图像。

序列化数据

序列化是将高级数据对象转化为二进制字符串的过程,反之则是反序列化。网络通信离不开序列化和反序列化。

使用场景

服务器动态生成数据

最常见,如数据库操作结果。考虑到节省的空间相当可观,因此客户端付出的解压时间开销是值得的。

服务器静态数据

如配置文件,不定期离线更新,在客户端请求时将其发送过去。

客户端动态生成数据

如手机的GPS定位、可穿戴设备如手环的运动数据。客户端承担序列化操作和数据压缩等开销;开发人员需要权衡电量损耗(压缩开销)和上传数据大小等因素。

客户端拥有的静态数据

布局信息:一次编写,多次加载,没有变化。

实践:保持数据驻留(或永久存储)并在需要时将其加载到内存中。

问题

JSON和XML。

可读文本

JSON和XML可读性很好,灵活(绝大多数数据结构可以找到序列化为这种格式的方法),缺点是包含很多冗余信息(空格、换行符、双引号、重复的键等)。

解码时间长

解码时间长的原因:

  • 字符串的输入必须经过强力操作才能转化为内存对象(如将ASCII符号转换为整数就不那么容易);
  • 在加载期间将数据保存在临时内存里并非总是高效的;
  • 对旧格式的兼容也会使得编码和解码变慢。

像XML和JSON格式都倾向于有较长的加载时间,以便客户端能正确地反序列化。事实上,存在不少XML和JSON编码器专注于减少特定组织的文件类型的加载时间。

更小的序列化数据

主要有四个技巧,如下三级标题所列。

使用二进制序列化格式

抛弃XML和JSON,找出一种二进制序列化格式来代替它们。可考虑的格式包括:Protobufs、Flatbuffers和Cap’n Proto,与人类可读格式相比,可以产生更好的压缩效果,并且在某些情况下,实际上还可以使用通用编码工具如GZIP对它进行进一步压缩。

坚持使用JSON或XML的话,可考虑引入BSON和MSGPACK这些格式。既保留JSON的模式,编码时又能提供二进制的大小。可得到更小的文件,而无须改动大量的代码。

重构列表以获得更好的压缩

一种常见的JSON文本可能是这样的:

[
	{
		"country": "USA",
		"name": "Joanna"
	},
	{
		"country": "AUS",
		"name": "Alex"
	}
]

不难得知country和name有大量的重复性冗余。

另一个有点难想到的点是:name或country大概率会出现重复的数据;毕竟世界范围内国家数量不到200个,常见的会少很多;名字也差不多,分男性和女性常用名。现在相似属性值之间的距离隔着很远,压缩算法很难派上用场。

优化后的JSON数据如下:

{
	"name": ["Joanna", "Alex"],
	"country": ["USA", "AUS"]
}

既减少冗余,也使LZ等算法更容易找到匹配。

用编程的语言来就是,对于大的序列化文件,将结构的数组转换为数组的结构极为重要。

组织数据以便高效获取

真的需要从服务器中获取完全结构化的数据吗?还是可以分别请求每种类型的数据(如果有必要的话,再在客户端将这些数据汇总起来)?

后端应用程序往往喜欢为所有的用户(PC、H5、移动端等)都提供通用的API,但对客户端来说并不那么友好。

后端开发者在设计接口时,要多考虑紧凑性。

客户端对需要显示的数据掌握的信息越多,效率就越高。应用程序决定缓存或者删除哪些数据,如怎样在新数据达到时使布局无效。移动客户端要比简单的HTML渲染器更强大,你完全可以将比较好的结构化数据交给它来处理。

将数据切分为适当的压缩格式

不要把什么内容都往JSON里面塞,应该保持JSON文本的轻量级,比如几KB,最多几十KB。

有损数据压缩

无损压缩算法,也就是说解码后的数据与源数据的每一位都相同。

有损压缩工具通常会被首先应用,以减少数据的动态变化范围,从而为进一步的无损压缩做准备。

有损压缩工具有很多,如何选择取决于需要处理的数据类型、需求、用户愿意容忍多大范围的失真。

让世界变得更小

数据压缩与你

数据压缩与每个人息息相关。

数据压缩与盈利

数据压缩的盈利:

  • 用户获取与保持
  • 运行成本
  • 提前规划

让用户的生活更美好更便宜

一切都是需要用户支付费用的。

电池的开销不容小觑。

对下一步技术的思考

与投入数百万美元升级网络硬件相比,投资更好的压缩解压编解码器要划算得多。

兴,百姓苦;亡,百姓苦。

开始行动

2015年,大公司如Facebook推出精简的、2G友好的版本,以减少在这一新兴移动市场中获取用户的障碍。

反观一下其他公司呢?

微信刚出来时非常简洁,安装包小,设计简洁,广告少……

现在呢?24年9月。一个iOS安装包就是700多M,各种功能(朋友圈、小程序、视频号、直播、游戏、看一看)眼花缭乱,关闭不了的广告。