一、实验原理
1.JPEG编码原理
JPEG编码的过程如下图所示。解码是编码的逆过程。
(1)零偏置:即把[0,255]的像素值减128变为[-128,127];
(2)8×8DCT变换:能量集中和去相关,减小空间冗余;
(3)量化:根据人眼视觉特性,低频细量化,高频粗量化,减小视觉冗余;
(4)编码:直流系数进行差分和VLC编码,交流系数进行之字形扫描、游程编码和VLC编码,减少数据冗余。
2.JPEG文件格式
(1)Segment 的组织形式
JPEG 在文件中以 Segment 的形式组织,它具有以下特点:
均以 0xFF 开始,后跟 1 byte 的 Marker 和 2 byte 的 Segment length(包含表示
Length 本身所占用的 2 byte,不含“0xFF” + “Marker” 所占用的 2 byte);
采用 Motorola 序(相对于 Intel 序),即保存时高位在前,低位在后;
Data 部分中,0xFF 后若为 0x00,则跳过此字节不予处理;
(2)JPEG 的 Segment Marker
3.JPEG 的解码流程
(1)读取文件
(2)解析 Segment Marker
(i)解析 SOI
(ii)解析 APP0
检查标识“JFIF”及版本
得到一些参数
(iii)解析 DQT
得到量化表长度(可能包含多张量化表)
得到量化表的精度
得到及检查量化表的序号(只能是 0 —— 3)
得到量化表内容(64 个数据)
(iv)解析 SOF0
得到每个 sample 的比特数、长宽、颜色分量数
得到每个颜色分量的 ID、水平采样因子、垂直采样因子、使用的量化表序号(与 DQT 中序号对应)
(v)解析 DHT
得到 Huffman 表的类型(AC、DC)、序号
依据数据重建 Huffman 表
(vi)解析 SOS
得到解析每个颜色分量的 DC、AC 值所使用的 Huffman 表序号(与 DHT中序号对应)
(3) 依据每个分量的水平、垂直采样因子计算 MCU 的大小,并得到每个 MCU 中 8*8宏块的个数
(4)对每个 MCU 解码(依照各分量水平、垂直采样因子对 MCU 中每个分量宏块解码)
(i) 对每个宏块进行 Huffman 解码,得到 DCT 系数
(ii) 对每个宏块的 DCT 系数进行 IDCT,得到 Y、Cb、Cr
(iii)遇到 Segment Marker RST 时,清空之前的 DC DCT 系数
(5)解析到 EOI,解码结束
(6)将 Y、Cb、Cr 转化为需要的色彩空间并保存
二、实验步骤
1.逐步调试JPEG解码器程序。将输入的JPG文件进行解码,将输出文件保存为可供YUVViewer观看的YUV文件。
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);
在write_yuv函数中加入如上代码,即可输出yuv文件,用yuvviewer打开后的图像如下
2.程序调试过程中,应做到:
理解程序设计的整体框架
理解三个结构体的设计目的
struct huffman_table
struct huffman_table
{
/* Fast look up table, using HUFFMAN_HASH_NBITS bits we can have directly the symbol,快速查找表,使用HUFFMAN_HASH_NBITS个比特可以直接找到符号
* 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];
};
struct component
struct component
{
unsigned int Hfactor;//水平采样
unsigned int Vfactor;//垂直采样
float *Q_table; /* Pointer to the quantisation table to use 指向量化表*/
struct huffman_table *AC_table;//指向交流Huffman查找表
struct huffman_table *DC_table;//指向直流Huffman查找表
short int previous_DC; /* Previous DC coefficient 前一个直流系数*/
short int DCT[64]; /* DCT coef DCT系数*/
#if SANITY_CHECK
unsigned int cid;
#endif
};
struct jdec_private
struct jdec_private
{
/* Public variables */
uint8_t *components[COMPONENTS];
unsigned int width, height; /* Size of the image */
unsigned int flags;
/* Private variables */
const unsigned char *stream_begin, *stream_end;
unsigned int stream_length;
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 直流Huffman表 */
struct huffman_table HTAC[HUFFMAN_TABLES]; /* AC huffman tables 交流Huffman表 */
int default_huffman_table_initialized;
int restart_interval;
int restarts_to_go; /* MCUs left in this restart interval 剩余的MCU*/
int last_rst_marker_seen; /* Rst marker is incremented each time 上一个marker*/
/* 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];
};
理解在视音频编解码调试中TRACE的目的和含义
会打开和关闭TRACE
#define snprintf _snprintf//add by nxn
#define TRACE 1//add by nxn trace设置1为打开,0为关闭
#define TRACEFILE "trace_jpeg.txt"//add by nxn
会根据自己的要求修改TRACE
在process_Huffman_data_unit函数最后添加以下代码
#if TRACE
fprintf(p_trace, "%d ", DCT[0]);
#endif
3.以txt文件输出所有的量化矩阵和所有的HUFFMAN码表。
4.输出DC图像并统计其概率分布。
5.输出某一个AC值图像并统计其概率分布。