JPEG编码原理及⽂件格式及代码分析
⼀ JPEG编码原理
⾸先我们先来看⼀下JPEG的编码原理图
如上图所⽰,下⾯进⾏逐步的分析:
1 RGB->YUV
⾸先为了降低互相的关联性,将RGB转换为YUV,这样就可以对亮度信号和⾊度信号进⾏分别的处理
2 零电平偏置下移
由于后⾯需要对图像进⾏DCT变换,如果不进⾏偏移,会使分量值过⼤,所以在这⾥采⽤偏置下移的⽅法,便于DCT变换量化后直流的系数⼤⼤降低,也就降低了数据量。
3 分块
JPEG标准在处理图⽚时会先把图⽚分割成⼀个个8x8像素的⽅块,源图像如果不是8x8的整数倍,需进⾏补充,后⾯的DCT、量化、熵编码都是针对单个⽅块的操作,编码的产物是这些⽅块的压缩数据。压缩数据经过解码还原成像素数据,然后将⼀个个⽅块拼成⼀整完整图⽚的像素数据显⽰。
4 DCT变换
fprintf格式对每⼀块进⾏DCT变化,⽬的是去除图像数据之间的相关性,便于量化过程去除图像数据的空间冗余。由于⼈眼对图⽚中的低频信息(⾊彩变化不明显,如图⽚的整体⾊调,物体轮廓)⽐较敏感,对⾼频信息不敏感(⾊彩变化剧烈,如物体的边缘、⼈脸上的⼩斑点),因此我们可以利⽤DCT变换把图⽚中⾼频和低频部分区分开来,然后将⾼频部分的数据进⾏压缩,这样就达到压缩图⽚的功能。
⼆维DCT变换公式如下所⽰
需要特别强调的是,DCT是⼀种⽆损变换,这样做的⽬的是在为下⼀步的量化做准备。
5 量化
所谓量化其实就是将频率/量化步长,将量化步长以内的精度信息丢失,JPEG算法提供了两张标准的量化系数矩阵,如下所⽰
可以看出越往左上⾓,值越⼩,因此这张量化表的作⽤就是屏蔽⾼频信息。
6 压缩
在这⾥直流分量与交流分量采⽤了不同的压缩⽅法。
对于直流分量来说,量化后每⼀块所得的数值⽐较⼤,但相邻块之间的差值⽐较⼩,这就很适合采⽤DPCM的压缩⽅式,在之前的博客中有较为详细的介绍,但DPCM之后还不够,还要进⾏霍夫曼编码,进⾏进⼀步的压缩
对于交流分量来说,每⼀块中剩余的交流分量都⽐较⼩,JPEG先⽤RLE(run-length encoding,游程编码)编码将图像数据以“之字形”排列,如下图,这样可以尽可能的将频率为0的数据存储在⼀起。连续N个0,可以⽤⼀个0和⼀个长度N来表⽰,压缩效果很好,然后将剩下的位置使⽤霍夫曼编码。
在这⾥说⼀句,JPEG的解码就是将其编码过程倒过来,进⾏⼀步⼀步的还原,在这⾥就不细说了。
⼆ JPEG⽂件格式
JPEG⽂件由⼀系列字段组成,每个字段都有marker(标记),由0xff开头。接下来对每⼀个字段进⾏简要的说明
1 SOI
这个字段定义了⽂件的起始标记,标记为FFD8。
2 APP0
Application,应⽤程序保留标记0,标志FFE0,包含⼀定信息
① 数据长度 2字节 ①~⑨9个字段的总长度
即不包括标记代码,但包括本字段
② 标识符 5字节 固定值0x4A46494600,即字符串“JFIF0”
③ 版本号 2字节 ⼀般是0x0102,表⽰JFIF的版本号1.2
可能会有其他数值代表其他版本
④ X和Y的密度单位 1字节 只有三个值可选
0:⽆单位;1:点数/英⼨;2:点数/厘⽶
⑤ X⽅向像素密度 2字节 取值范围未知
⑥ Y⽅向像素密度 2字节 取值范围未知
⑦ 缩略图⽔平像素数⽬ 1字节 取值范围未知
⑧ 缩略图垂直像素数⽬ 1字节 取值范围未知
⑨ 缩略图RGB位图 长度可能是3的倍数 缩略图RGB位图数据
3 DQT
也就是量化表,JPEG⽂件⼀般有2个DQT段,为Y值(亮度)定义1个, 为UV值(⾊度)定义1个。每张量化表都由FFDB开始,量化表中的第⼀个字节被分成了⾼四位和低四位来⽤。⾼四位表⽰了该量化表的精度,0:8位;1:16位;低四位表⽰了量化表ID,取值范围为0~3;接下来是所有的表项,
数量为(64×(精度+1))字节,⾥⾯都是量化的系数。量化表中的数据按照Z字形保存量化表内8x8的数据
4 SOF0
标志位FFC0,包含图像基本信息
段标识 1 FF
段类型 1 C0
段长度 2 其值=8+组件数量×3
  (以下为段内容)
样本精度 1 8 每个样本位数(⼤多数软件不⽀持12和16)
图⽚⾼度 2
图⽚宽度 2
组件数量 1 3 1=灰度图,3=YCbCr/YIQ 彩⾊图,4=CMYK 彩⾊图
  (以下每个组件占⽤3字节)
组件 ID 1 1=Y, 2=Cb, 3=Cr, 4=I, 5=Q
采样系数 1 0-3位:垂直采样系数
4-7位:⽔平采样系数
量化表号 1
5 DHT
标志位FFC4,存放哈夫曼表,JPEG⽂件⾥有2类Haffman 表:⼀类⽤于DC(直流量),⼀类⽤于AC(交流量)。⼀般有4个表:亮度的DC和AC,⾊度的DC和AC。最多可有6个。该Marker以FFC4作为开始标记。然后是字段长度,类型(AC/DC),索引(Index),位表(bit table),值表(value table)。
6 SOS
扫描⾏开始,标志位FFDA,表明了字段的长度,然后说明了颜⾊分量数,该与SOF字段中的数据应该是保持⼀致的。然后针对于每⼀个颜⾊分量信息,给出了每个分量的DC/AC使⽤的哈夫曼表编号。
三 代码解读
本次实验主要做到了JPG⽂件转换为YUV⽂件,并且输出了量化系数以及霍夫曼码表。
⾸先先来看⼀下如何写⼊YUV⽂件,原先的代码为分别⽣成.Y .U .V⽂件,其实只需要将这三个写⼊的数据按照YUV的顺序写⼊YUV⽂件即可,具体代码如下所⽰
static void write_yuv(const char *filename, int width, int height, unsigned char **components)
{
FILE*F;
char temp[1024];
snprintf(temp,1024,"%s.Y", filename);
F=fopen(temp,"wb");
fwrite(components[0], width, height,F);
fclose(F);
snprintf(temp,1024,"%s.U", filename);
F=fopen(temp,"wb");
fwrite(components[1], width*height/4,1,F);
fclose(F);
snprintf(temp,1024,"%s.V", filename);
F=fopen(temp,"wb");
fwrite(components[2], width*height/4,1,F);
fclose(F);
snprintf(temp,1024,"%s.YUV", filename);
F=fopen(temp,"wb");
fwrite(components[0], width, height,F);
fwrite(components[1], width * height /4,1,F);
fwrite(components[2], width * height /4,1,F);
fclose(F);
}
结果如下所⽰
原图像为
⽣成的YUV图像为
最主要的是观看量化系数的步骤,⾸先先来看⼀下主函数中调⽤的convert_one_image函数

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。