实验目的
- 掌握JPEG编解码系统的基本原理。
- 初步掌握复杂的数据压缩算法实现,并能根据理论分析需要实现所对应数据的输出。
实验原理
1.编码原理
基本流程:
1.亮度信号零偏置电平下移,对于灰度级是2n的像素,通过减去2n-1,将无符号的整数值变成是有符号数。这样做的目的是使像素的绝对值出现3位10进制的概率大大减少。
2.DCT变换
对每个单独的彩色图像分量,把整个分量图像分成8×8的图像块,并进行8×8DCT变换,目的是去除图像数据的相关性,便于量化过程去除图像数据的空间冗余。
3.量化
利用人眼视觉特性设计而成的矩阵量化DCT系数,减小视觉冗余。因为人眼对亮度信号比色差信号更敏感,因此使用了两种量化表:亮度量化值和色差量化值;根据人眼对低频敏感,对高频不太敏感,对低频分量采取较细的量化,对高频分量采取较粗的量化。
4.熵编码
(1)对量化后的DC系数进行差分编码
8×8图像块经过DCT变换之后得到的DC直流系数有两个特点:系数的数值较大;相邻8×8图像块的DC系数值变化不大。根据这个特点,JPEG算法使用了差分脉冲调制编码(DPCM)技术对相邻图像块之间量化DC系数的差值进行编码。
(2)AC系数进行Z字扫描和游程编码后,再分别进行VLC编码
Z字扫描:由于DCT变换后,系数大多数集中在左上角,即低频分量区,因此采用Z字形按频率的高低顺序读出,可以出现很多连零的机会,可以使用游程编码。
游程编码:在JPEG和MPEG编码中规定为(run,level):表示连续run个0,后面跟值为level的系数。
2.JPEG文件格式
JPEG 在文件中以 Segment 的形式组织,它具有以下特点:
均以 0xFF 开始,后跟 1 byte 的 Marker 和 2 byte 的 Segment length(包含表示 Length 本身所占用的 2 byte,不含“0xFF” + “Marker” 所占用的 2 byte);
采用 Motorola 序(相对于 Intel 序),即保存时高位在前,低位在后;
Data 部分中,0xFF 后若为 0x00,则跳过此字节不予处理;
3.量化表DQT
一般为两个量化表,即亮度和色度各一张
以0xFFDB开始
➢量化表长度,一般为00 43(或00 84)
➢量化表信息(1字节)
◼ Bit 0~3 QT号(只能取值为0~3,否则错误)
◼ Bit 4~7 QT精度(0为8比特,否则表示16比特)
量化表的实际数据
◼ 量化表中的数据按照Z字形保存量化表内8x8的数
4.帧图像开始SOF0
5.一个或多个Huffman表,数值0xFF C4
实验步骤
1.调试JPEG编码器,解码输入的JPEG文件并将文件输出至YUV格式
对解码器输入参数 test1.jpg yuv420p test1.yuv
将文件保存至YUV格式
原始程序中文件会分别保存为.Y .U .V三个独立文件,在函数write_yuv(const char *filename, int width, int height, unsigned char **components)中添加如下代码:
snprintf(temp, 1024, “%s.YUV”, filename); //输出一个完整的YUV文件
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);
2.理解三个结构体的含义
struct huffman_table
目的:储存解码所用的Huffman码表,codesize储存码长
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 */
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];
};
struct component
目的:存储每个88系数块信息,用于该系数块的解码,包含DC、AC Huffman码表,量化表,色度采样格式和88DCT系数。其中DC系数采用差分编码。
struct component
{
unsigned int Hfactor; /* Horizontal factor */
unsigned int Vfactor; /* Vertical factor */
/** 每个component 的Hfactor 和 Vfactor共同决定色度采样格式 **/
float *Q_table; /* Pointer to the quantisation table to use */
struct huffman_table *AC_table; /* Pointer to the AC table to use */
struct huffman_table *DC_table; /* Pointer to the DC table to use */
/* 量化表、Huffman表均为指针,指向结构体jdec_private中实际数据 */
short int previous_DC; /* Previous DC coefficient */
short int DCT[64]; /* DCT coef */
#if SANITY_CHECK
unsigned int cid;
#endif
};
struct jdec_private
目的:存储Jpeg图片的基本尺寸信息、数据、量化表、Huffman表
struct jdec_private
{
/* Public variables */
uint8_t *components[COMPONENTS];
/* 指针数组,指向3个分量数据 */
unsigned int width, height; /* 图片尺寸 */
unsigned int flags;
/* Private variables */
const unsigned char *stream_begin, *stream_end;
unsigned int stream_length;
/* stream_begin指示文件在内存中起始地址,stream_end指示结束地址, stream_length 指示除去0xFF和0xD8(SOI)两字节文件长度 */
const unsigned char *stream; /* Pointer to the current stream */
unsigned int reservoir, nbits_in_reservoir;
struct component component_infos[COMPONENTS];
float Q_tables[COMPONENTS][64]; /* quantization tables */
struct huffman_table HTDC[HUFFMAN_TABLES]; /* DC huffman tables */
struct huffman_table HTAC[HUFFMAN_TABLES]; /* AC huffman tables */
int default_huffman_table_initialized;
int restart_interval;
int restarts_to_go; /* MCUs left in this restart interval */
int last_rst_marker_seen; /* Rst marker is incremented each time */
/* Temp space used after the IDCT to store each components */
uint8_t Y[64*4], Cr[64], Cb[64];
jmp_buf jump_state;
/* Internal Pointer use for colorspace conversion, do not modify it !!! */
uint8_t *plane[COMPONENTS];
};
3.以txt文件输出所有的量化矩阵和所有的HUFFMAN码表
4.输出DC图像并统计其概率分布;输出某一个AC值图像并统计其概率分布
选取DCT系数矩阵中(4,4)位置进行统计,结果如下:
原图像
原图灰度直方图
由结果可以看出,由每个8*8DCT块中直流分量组成的图像分布近似于原图灰度直方图分布,而某一交流分量分布近似于拉普拉斯分布。其结果与理论相符