JPEG编解码基本技术回顾
JPEG格式是一个很老的格式了,笔者刚刚认识5寸软盘的时代就知道这种文件是用来保存图片的,而且比同尺寸的“bmp”图片要小很多。很多年过去了,信息技术各个领域全面发展,视频编解码技术更新换代了好几轮,然而大家从网页上看到的、自己磁盘里存储的、照相机/手机拍摄的,仍然以这种格式的图片为主。
为什么JPEG能够流行这么长的时间呢?一个很重要的原因固然是它流行得很早,但另外一个原因则是这种图片编码格式体现了基于主流图片/视频压缩技术最根本的世界观和方法论。本文就以JPEG中最基本最常用的Baseline-DCT模式为例,简单回顾下JPEG编解码技术的整体概况和若干细节,并和当今的视频编解码技术做一个对照。
一. 基本概况
JPEG支持多种编解码模式。最基本的Baseline-DCT支持8比特sample,熵编码采用哈夫曼编码,然而扩展DCT模式则能够支持12比特的sample,熵编码既可以用哈夫曼编码也可以用算术编码。
采样格式方面,支持纯亮度,YUV420,YUV422和YUV444。
分块方面,一律8x8大小分块。
JPEG标准可从ITU-T官网下载,标准编号是T.81。
二. 格式
JPEG文件中的语法单位基本是由marker来组织的。每个marker都是由0xFF开头的一个16位整数,标记接下来的数据内容。对于熵编码部分,如果出现了0xFF则要填充一个0x00。
下面列一些重要的marker:
SOI:即start of image的意思。应出现在文件最开头。纯marker。
SOFn:即start of frame。n取值0到15。其中SOF0即为最基本的Baseline-DCT。
DQT:量化表。大小和块的大小一致(8x8)。编码时除解码时乘。
DHT:哈夫曼编码表。
SOS:即start of scan。一个frame可分为多个scan。
DNL:用于分隔scan。
RSTm:若出现在熵编码中,可使熵编码过程重新开始。m取0~7。
EOI:即end of image。应出现在文件结尾。
总体结构如下图:
其中Tables主要指的就是DQT和DHT。ECS就是熵编码段落。MCU指的就是编码的块。
三. 块的编解码流程
对于编码来讲,过程如下
- 预测
- DCT变换
- Zigzag扫描
- 量化
- 熵编码
下面挨个介绍:
- 预测。很多人说JPEG没有帧内预测,所以其压缩效率才赶不上当今的技术。然而事实上JPEG在进行DCT变换前会将块内所有值都减去128。这样做相当与128就是所有块的预测值,减去128后得到的就是残差数据。从这个角度看JPEG算是有空域上的预测。HEVC在帧内预测中,如果某PU参考的采样点都无法获取,也会将预测值设成128。
- DCT。8x8二维离散余弦变换。
- Zigzag扫描。从左上角到右下角扫描。
- 量化。块中每个值除以当前量化表中对应位置的值。这里会有信息损失。注意zigzag的顺序不能和量化搞反。
- 熵编码。最复杂的步骤。DC分量和AC系数的编码方式不同。DC分量会在各块间建立一个差分序列,然后对残差进行编码(也相当于一种频域上的帧内预测),先是进行Exp-Golomb编码,然后Huffman编码。AC系数则先变成RRRRSSSS的8比特形式。其中RRRR表示前面有多少个0系数,SSSS为后面的非0系数的指数哥伦布编码。然后把RRRRSSSS整个进行Huffman编码。如果当前块剩下的AC系数全部是0,则RRRRSSSS等于0;否则若0系数个数大于等于16个,则RRRRSSSS=0xF0,表示16个0。
而解码过程需要将上述步骤的顺序和内容都反过来,即反熵编码、反量化、反zigzag、反DCT、反预测。
可以看出与当今的技术用的都是一种套路。
熵编码中多个component则按照Y、U、V的顺序,逐个plane进行编码。而色度的扫描方式要先把一个亮度大小的方块区域内按照从左到右从上到下的顺序编码完成再进行下一个亮度方块内的色度编码。如下图所示:
这里面值得一提的是,AVC和HEVC用熵编码过程中都使用了一种自适应的二进制算术编码(CABAC),Huffman编码由于只有在每个符号概率在2的n次方时才能够达到最理想压缩比,同时缺少自适应机制,压缩比会照CABAC差不少。但实际上JPEG标准中的对于扩展DCT模式定义了一种自适应的算术编码的算法,其基本原理和CABAC一致,但是其执行效率和压缩比都照CABAC差了一些。据统计使用了算术编码的JPEG比baseline-JPEG缩小了%4~%6。后来JPEG又进一步改进了该算法。
四 . 优缺点
第一个缺点是压缩得不彻底(跟目前的技术比)。各个块之间,无论是DC分量还是各个AC分量,在进入熵编码阶段时仍然具有一定的相关性。
第二个缺点是artifect严重。在压缩比较高的情况下,在图片平缓的部分由于低频系数的信息损失会产生严重的blocking效果;而在图片具有强烈边缘的地方由于高频系数的信息损失则会产生严重的ringing效果。JPEG标准没有定义任何针对artifect的滤波器。
第三个缺点则是没有支持alpha通道。这个问题使得在需要alpha通道时,即便对于照片类的图片人们也会选择png格式。但实际上如果JPEG支持alpha的话,照片类图片很可能无论是压缩效率上还是解码速度上都强于png。
最后一个缺点也是所有有损压缩的缺点,每次压缩都会损失一部分信息。png压缩也会丢失信息,但是当你把它解码出来再用同样的参数压缩时,不会有额外的信息损失了;但是JPEG每次编码都会有损失。所以说大家的表情包越传越模糊发绿,也是这个原因。
那么优点是什么呢?就是简单,编解码速度超快,适合并行计算。目前智能手机虽然性能提高了很多了,但是要让CPU实时跑HEVC编码还是非常勉强(也许以后都会用硬编硬解?),但是对JPEG的编解码却完全没有问题。libjpeg-turbo这个库大概是目前最快的jpeg编解码库了,利用了主流架构的SIMD指令,很适合移动端使用。
五. 可能的替代者
WebP。谷歌的格式,衍生自谷歌的VP8视频编码格式。支持clipart,支持动画,支持alpha通道。据说压缩效率比JPEG高,但是也有人指出其实跟JPEG差不多。但总归是比png要好。个人觉得它代替png和gif更合适些。另外libwebp的api设计得很不好用,速度也慢。
HEIF。开发者是MPEG。苹果公司力推的格式,在苹果电脑上已经支持。原理其实就是把HEVC的单帧压缩技术拿出来用。可以想象得到,其压缩效率应该是极高的(应该能达到JPEG的55%),但同时运算复杂度也会很高。
BPG。开源项目,还在开发中。和HEIF一样都是HEVC的单帧压缩技术。然而由于HEVC的各个部分都被专利保护,因此这种格式前途未卜。
packJPG。这是一种专门针对JPEG文件进行无损压缩的格式。比baseline-JPEG大小缩减大约20%左右,可以说是非常可观了。它更充分地利用了JPEG中一些属性间的相关性。
Lepton。Dropbox公司在packJPG的基础上开发的格式,同样是专门针对JPG的。比baseline-JPEG大小缩减大约23%左右,同时相对于packJPG大大提高了编解码速度。用于节省Dropbox中JPEG文件的存储空间。
Pik。怀疑为谷歌某些工程师的业余项目。其目标是在保证解码速度不低于JPEG 40%的情况下,大小缩减为JPEG的65%。目前正在开发中,据说使用了一些新技术。
个人觉得JPEG的替代者应该继承JPEG简单高速的优点。上述列表中的Lepton和Pik是比较契合这一点的。仍然应该避免进行复杂帧内预测以及由此带来的R-D优化。