JPEG 原理分析及 JPEG 解码器的调试
一、实验原理
1、JPEG编码原理
(1)DCT变换:能量守恒,能量集中,去相关
从框图可以看出先进行电平偏移再进行8x8块的DCT变换。level offset的原因在于Y的范围为(0,255),而U、V的范围为(-128,128),所以Y的电平应该减去128,再分为8x8的块进行DCT变换。
DCT变换的基本思路是将图像分解为8×8的子块或16×16的子块,并对每一个子块进行单独的DCT变换,然后对变换结果进行量化、编码。随着子块尺寸的增加,算法的复杂度急剧上升,因此,实用中通常采用8×8的子块进行变换。
(2)量化
<1>采用中平型均匀量化器量化
<2>量化步距是按照系数(所在的位置、颜色分量) 来确定。
因为人眼对亮度信号比对色差信号更敏感,因此使用了两种量化表:亮度量化值和色差量化值
<3>根据人眼的视觉特性(对低频敏感,对高频不太敏感)对低频分量采取较细的量化,对高频分量采取较粗的量化(如果原始图象中细节丰富,则去掉的数据较多,量化后的系数与 量化前差别;反之,细节少的原始图象在压缩时去掉的数据少些)
真正的量化表=缩放因子×基本量化表
质量因子≤ 50:缩放因子= 50 / 质量因子
质量因子> 50:缩放因子 = 2 – 质量 因子/ 50
(3)DC系数的差分编码
8×8图像块经过DCT变换之后得到的DC直流系数有两个特点:
系数的数值比较大
相邻8×8图像块的DC系数值变化不大:冗余
根据这个特点,JPEG算法使用了差分脉冲调制编码 (DPCM)技术,对相邻图像块之间量化DC系数的差值 DIFF进行编码:
(4)AC系数的Z字扫描和游程编码
由于经DCT变换后 ,系数大多数集中在左上角,即低频分量区,因此采用Z 字形按频率的高低顺序读出,可以出现很多连零的机会 。可以使用游程编码。尤其在最后, 如果都是零,给出EOB (End of Block) 即可。
在JPEG和MPEG编码中规定为:(run, level)
表示连续run个0,后面跟值为level的系数
如:0,2,0,0,3,0,-4,0,0,0,-6,0,0,5,7
表示为(1, 2), (2, 3) ,…
编码:
Run: 最多15个,用4位表示RRRR
Level:类似DC
分成16个类别,用4位表示SSSS表示类别号
类内索引 对(RRRR, SSSS)联合用Huffman编码
对类内索引用定长码编码
2、解码流程
解码Huffman数据
解码DC差值
重构量化后的系数
DCT逆变换
丢弃填充的行/列
反0偏置
对丢失的CbCr分量差值(下采样的逆过程)
YCbCr to RGB
3、JPEG文件格式
二、实验流程
1、逐步调试JPEG解码器程序。将输入的JPG文件进行解码,将输出文件保存为可供 YUVViewer观看的YUV文件。
2、程序调试过程中,应做到:
理解程序设计的整体框架
理解三个结构体的设计目的
struct huffman_table
struct component
struct jdec_private
理解在视音频编解码调试中TRACE的目的和含义
会打开和关闭TRACE
会根据自己的要求修改TRAC
3、以txt文件输出所有的量化矩阵和所有的HUFFMAN码表。
4、 输出DC图像并经过huffman统计其概率分布(使用第三个实验中的Huffman编码器)。
5、输出某一个AC值图像并统计其概率分布(使用第三个实验中的Huffman编码器)。
三、关键代码分析
1、将输入的JPG文件进行解码,将输出文件保存为可供 YUVViewer观看的YUV文件。
头文件
enum tinyjpeg_fmt {
TINYJPEG_FMT_GREY = 1,
TINYJPEG_FMT_BGR24,
TINYJPEG_FMT_RGB24,
TINYJPEG_FMT_YUV420P,
TINYJPEG_FMT_YUV420ALL,
};
函数
int main(int argc, char *argv[])
{
int output_format = TINYJPEG_FMT_YUV420ALL;//将输出的格式换为yuv文件格式
...
input_filename = argv[current_argument];
if (strcmp(argv[current_argument+1],"yuv420p")==0)
output_format = TINYJPEG_FMT_YUV420P;
else if (strcmp(argv[current_argument+1],"rgb24")==0)
output_format = TINYJPEG_FMT_RGB24;
else if (strcmp(argv[current_argument+1],"bgr24")==0)
output_format = TINYJPEG_FMT_BGR24;
else if (strcmp(argv[current_argument+