一、实验目的
掌握JPEG编解码系统的基本原理。初步掌握复杂的数据压缩算法实现,并能根据理论分析需要实现所对应数据的输出。
二、理论知识
1.JPEG编码原理
(1)Level offset(零电平偏置)
(2)8*8DCT变换
(3)用根据人眼视觉特性设计的量化矩阵对DCT变换之后的结果进行量化
(4)
DC 系数编码: 由于直流系数 F(0,0)反映了该子图像中包含的直流成分,通常较大,又由 于两个相邻的子图像的直流系数通常具有较大的相关性,所以对 DC 系数采用 差值脉冲编码(DPCM),即对本像素块直流系数与前一像素块直流系数的差 值进行无损编码。
AC 系数编码: 首先,进行游程编码(RLC),并在最后加上块结束码(EOB);然后,系 数序列分组,将非零系数和它前面的相邻的全部零系数分在一组内;每组用两 个符号表示[(Run,Size),(Amplitude)]
2.JPEG解码流程
(1) 读取文件
(2)解析 Segment Marker
a 解析SOI
b 解析APP0,检查标识“JFIF”及版本,得到一些参数
c 解析DQT,得到量化表长度(可能包含多张量化表),得到量化表的精度 ,得到及检查量化表的序号(只能是 0 —— 3),得到量化表内容(64 个数据)
d 解析 SOF0,得到每个 sample 的比特数、长宽、颜色分量数 得到每个颜色分量的 ID、水平采样因子、垂直采样因子、使用的量化表 序号(与 DQT 中序号对应)
e 解析DHT,得到 Huffman 表的类型(AC、DC)、序号,依据数据重建 Huffman 表
f 解析SOS,得到解析每个颜色分量的 DC、AC 值所使用的 Huffman 表序号(与 DHT 中序号对应)
(3)依据每个分量的水平、垂直采样因子计算 MCU 的大小,并得到每个 MCU 中 8*8 宏块的个数
(4)对每个MCU解码(依照各分量水平、垂直采样因子对MCU中每个分量宏块解码)
a 对每个宏块进行Huffman解码,得到DCT系数
b 对每个宏块的 DCT 系数进行 IDCT,得到 Y、Cb、Cr
c 遇到 Segment Marker RST时,清空之前的 DC DCT 系数
(5) 解析到 EOI,解码结束
(6) 将 Y、Cb、Cr 转化为需要的色彩空间并保存。
3.JPEG文件格式简述
(1)JPEG 在文件中以 Segment 的形式组织,它具有以下特点:
均以 0xFF 开始,后跟 1 byte 的 Marker 和 2 byte 的 Segment length(包含表示 Length 本身所占用的 2 byte,不含“0xFF” + “Marker” 所占用的 2 byte);
采用 Motorola 序(相对于 Intel 序),即保存时高位在前,低位在后;
Data 部分中,0xFF 后若为 0x00,则跳过此字节不予处理;
(2)Segment Marker
三、实验内容与相关结果
1.JPEG编解码原理以及结合C++程序的设计框架理解
2.逐步调试JPEG解码器程序。将输入的JPG文件进行解码,将输出文件保存为可供YUVViewer观看的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);
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);
}
如图为jpg图像与解码输出的yuv图像(yuv图像按1024*1024显示)
3.三个结构体的设计目的
struct huffman_table–用于优化程序运行速度,lookup加速查表,失败则慢速查表;code_size指示已编码长度。
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–用于储存当前8×8像块的编码信息,便于解码等过程使用。Hfactor,Vfactor为水平,垂直采样因子。Q_table指向当前8×8块使用的量化表,Huffman_table对应当前块AC huffman表和DC huffman表,previous_DC为前一个宏块得DC系数,DCT[64]即存储了该宏块中8×8得DCT系数数值。
struct component
{
unsigned int Hfactor;
unsigned int Vfactor;
float *Q_table; /* Pointer to the quantisation table to use */
struct huffman_table *AC_table;
struct huffman_table *DC_table;
short int previous_DC; /* Previous DC coefficient */
short int DCT[64]; /* DCT coef */
#if SANITY_CHECK
unsigned int cid;
#endif
};
struct jdec_private–JPEG流结构体,在主程序中用此结构体存储解码后得到的JPEG流。包括以上两个结构体的信息以及图像的宽高信息,流长度信息,量化表和Huffman表信息等。
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 */
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];
};
4.在视音频编解码调试中TRACE的目的和含义
含义:trace在程序运行过程中记录下重要的信息,如解析DHT,解析DQT,采用的采样频率等
目的:便于后期程序调试
会打开和关闭TRACE—
此处定义为0即可关闭
#define TRACE 1
会根据自己的要求修改TRACE—
TRACE为1时,可以根据实际需要修改程序中形如下代码中xxx;处的内容
#if TRACE
//xxx;
#endif
5.以txt文件输出所有的量化矩阵和所有的HUFFMAN码表
6.输出DC图像并统计其概率分布
7.输出某一个AC值图像并统计其概率分布