一.实验目的
掌握JPEG编解码系统的基本原理。初步掌握复杂的数据压缩算法实现,并能根据理论分析需要实现所对应数据的输出。
**
二.JPEG编码过程
**
2.1 RGB to YUV
为了减少各分量之间的相关性,减少数据的冗余,通常会把RGB颜色空间转换成YUV来进行各分量的编码。
2.2 Level Offset 零偏置
对于灰度级是 2^n 的像素,通过减去 2^n-1 ,将无符号的整数值变成有符号数。
例如:n=8,灰度级0 ~ 255,通过减去128,转化为-128 ~ 127
作用:使像素的绝对值出现3位10进制的概率大大降低。
2.3 8x8 DCT变换
作用:
去除图像数据之间的相关性,便于量化过程去除图像数据的空间冗余。
离散DCT变换的变换核矩阵为:
2.4 量化
采用中平型均匀量化器
因为人眼对亮度信号比对色差信号敏感,因此使用两种量化表:亮度量化值和色差量化值。
根据人眼视觉特性(对低频敏感,对高频不太敏感),对低频分量采取较细的量化,对高频分量采取较粗的量化。
2.5 DC系数的差分编码
8 * 8图像块经过DCT变换后得到的DC系数有两个特点:
系数数值比较大;
相邻 8 * 8 图像块的DC系数值变化不大——冗余
根据这个特点,JPEG算法使用了DPCM技术,对相邻图像块之间量化DC系数的差值DIFF进行Huffman编码:
2.6 AC系数的zizag扫描
由于经过DCT变换后,系数大多数集中在左上角,即低频分量区,因此采用z字形按频率由低到高的顺序读出,可以出现很多连零的机会。在编码之前,需要把二维的变换系数矩阵转换为一维序列,由于量化之后右下角高频系数大部分为零,采用ZigZag Scan读取可以制造较长的零游程,提高编码效率。在扫描中,如果后续的系数全部为零,则用“EOB”表示块结束。
2.7 游程编码
游程编码是一种比较简单的压缩算法,其基本思想是将 重复且连续出现多次 的字符使用(连续出现次数,某个字符)来描述。
2.8 Huffman编码
在进行了RLE后,仍然需要进行Huffman编码。对于任何一个RLE的数据对,如(0,57),都可以表示成(RRRR,SSSS)的形式。其中前面的0-16采用自然码RRRR,后面的SSSS则是与DC一致的Huffman分组的编码方式,存储索引。放到码流里的是其组内编码。
最后总共有4张Huffman码表(亮度DC,亮度AC,色度DC,色度AC)。
三.JPEG码流结构分析
JPEG在文件中以Segment的形式组织 ,它具有以下特点:
均以0xFF开始,后跟1byte的Marker和2byte的Segment length(包含表示Length本身所占用的2byte,不含0xFF+Marker所占用的2byte);
采用 Motorola序(相对于Intel序),即保存时高位在前,低位在后;
Data部分中,0xFF后若为0x00,则跳过此字节不予处理。
四、代码实现
4.1程序准备
遵循程序要求,在调试属性内设定好命令参数。
第一个参数为要解码的jpg文件;
第二个参数为功能选项,此处选择yuv420p,即转换为yuv420格式的文件;
第三个参数为输出文件。
将输出文件保存为可供YUVViewer观看的YUV文件
5.1 三个重要结构体
1 struct huffman_table
struct huffman_table
{
/* Fast look up table, using HUFFMAN_HASH_NBITS bits we can have directly the symbol,
*快查找表
* if the symbol is <0, then we need to look into the tree table
*如果符号小于0,则在慢查找表中查找*/
short int lookup[HUFFMAN_HASH_SIZE];//得到权值对应的码字
/* code size: give the number of bits of a symbol is encoded */
unsigned char code_size[HUFFMAN_HASH_SIZE];//得到权值对应的码长
/* some place to store value that is not encoded in the lookup table
* FIXME: Calculate if 256 value is enough to store all values
*给出在快查找表中没有出现的码字*/
uint16_t slowtable[16-HUFFMAN_HASH_NBITS][256];
};
用来存储哈夫曼码表,分为快查找表和慢查找表,主要目的是提高解码效率。
2.struct component