一.实验目的
掌握JPEG编解码系统的基本原理。初步掌握复杂的数据压缩算法实现,并能根据理论分析需要实现所对应数据的输出。
二.实验内容
1.JPEG编解码原理
*JPEG编码的过程如上图所示。解码是编码的逆过程。
三 .JPEG 的解码流程
3.1 读取文件
3.2 解析 Segment Marker
3.2.1 解析 SOI
3.2.2 解析 APP0
检查标识“JFIF”及版本 得到一些参数
3.2.3 解析 DQT
得到量化表长度(可能包含多张量化表)
得到量化表的精度
得到及检查量化表的序号(只能是 0 —— 3)
得到量化表内容(64 个数据)
3.2.4 解析 SOF0
得到每个 sample 的比特数、长宽、颜色分量数
得到每个颜色分量的 ID、水平采样因子、垂直采样因子、使用的量化表
序号(与 DQT 中序号对应)
3.2.5 解析 DHT
得到 Huffman 表的类型(AC、DC)、序号
依据数据重建 Huffman 表
3.2.6 解析 SOS
得到解析每个颜色分量的 DC、AC 值所使用的 Huffman 表序号(与 DHT
中序号对应)
3.3 依据每个分量的水平、垂直采样因子计算 MCU 的大小,并得到每个 MCU 中 8*8
宏块的个数
3.4 对每个 MCU 解码(依照各分量水平、垂直采样因子对 MCU 中每个分量宏块解
码)
3.4.1 对每个宏块进行 Huffman 解码,得到 DCT 系数
3.4.2 对每个宏块的 DCT 系数进行 IDCT,得到 Y、Cb、Cr
3.4.3 遇到 Segment Marker RST 时,清空之前的 DC DCT 系数
3.5 解析到 EOI,解码结束
3.6 将 Y、Cb、Cr 转化为需要的色彩空间并保存。
四.代码实现
步骤一:
逐步调试JPEG解码器程序。将输入的JPG文件进行解码,将输出文件保存为可供YUVViewer观看的YUV文件。
sprintf函数用于格式化写入字符串,然后将component[]内的所有yuv内容写入总体的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);
fclose(F);
** 添加部分
snprintf(temp, 1024, "%s.YUV", filename);
F = fopen(temp, "wb");
fwrite(components[0], width* height, 1,F);
fwrite(components[1], width * height / 4, 1, F);
fwrite(components[2], width * height / 4, 1, F);
fclose(F);**
}
参数设置:
实验效果
原图:
输出的yuv文件:
步骤二:
目标一:理解程序设计的整体框架
读入文件名以及输出格式后来到convert_one_image函数:判断是否成功打开文件,获得文件长度,是否成功读入缓存,调用tinyjpeg.c函数获取文件大小以及component。
tinyjpeg_parse_header函数判断文件开头是否满足jpg条件后,获得SOI后的数据地址,文件长度信息。
调用parse_JFIF函数分块解析
解析DQT块进入parse_DQT通过 build_quantization_table建立量化表,只支持小于4张量化表,每个表64个数据
解析DHT块进入parse_DHT,通过 build_huffman_table建立哈夫曼表
解析 SOS调用parse_SOS,解析每个颜色分量的 DC、AC 值所使用的 Huffman 表序号(与 DHT中序号对应)。
调用 tinyjpeg_decode进行解码:3.4.1 对每个宏块进行 Huffman 解码,得到 DCT 系数,对每个宏块的 DCT 系数进行 IDCT,得到 Y、Cb、Cr。遇到 Segment Marker RST 时,清空之前的 DC DCT 系数。解析到 EOI,解码结束。
最后将 Y、Cb、Cr 转化为需要的色彩空间并保存。
目标二:理解三个结构体的设计目的
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
code size是符号加密后比特数 */
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;交流哈夫曼表
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用于定义各种指针解码数据流
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];
};
目标三:理解在视音频编解码调试中TRACE的目的和含义
TRACE的作用主要是定点纠错,在trace=1时通过if trace{输出内容}end if可以输出所需参数,进一步理解解码过程。
目标四:以txt文件输出所有的量化矩阵和所有的HUFFMAN码表。
目标五:输出DC图像并统计其概率分布。