实验目的
掌握JPEG编解码系统的基本原理。初步掌握复杂的数据压缩算法实现,并能根据理论分析需要实现所对应数据的输出。
实验内容
1.JPEG编解码原理
JPEG编码的过程如上图所示。解码是编码的逆过程。
2.JPEG文件格式
Segment 的组织形式:
JPEG 在文件中以 Segment 的形式组织,它具有以下特点:
(1)均以 0xFF 开始,后跟 1 byte 的 Marker 和 2 byte 的 Segment length(包含表示
Length 本身所占用的 2 byte,不含“0xFF” + “Marker” 所占用的 2 byte);
(2)采用 Motorola 序(相对于 Intel 序),即保存时高位在前,低位在后;
(3)Data 部分中,0xFF 后若为 0x00,则跳过此字节不予处理;
JPEG 的 的 Segment Marker
3 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 转化为需要的色彩空间并保存。
实验步骤
1.逐步调试JPEG解码器程序。将输入的JPG文件进行解码,将输出文件保存为可供YUVViewer观看的YUV文件。
2. 程序调试过程中,应做到:
理解程序设计的整体框架
理解三个结构体的设计目的
• struct huffman_table
• struct component
• struct jdec_private
理解在视音频编解码调试中TRACE的目的和含义
• 会打开和关闭TRACE
• 会根据自己的要求修改TRACE
3.以txt文件输出所有的量化矩阵和所有的HUFFMAN码表。
4. 输出DC图像并统计其概率分布。
5. 输出某一个AC值图像并统计其概率分布。
实验代码及结果
1.将loadjpeg.c中的write_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, F);
fwrite(components[1], width * height / 4, 1, F);
fwrite(components[2], width * height / 4, 1, F);
fclose(F);
}
然后修改命令参数设置:
输出结果:
2.三个结构体和TRACE:
struct huffman_table:存储Huffman码表。
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像块中有关解码的信息。
struct component
{
unsigned int Hfactor; // 水平采样因子
unsigned int Vfactor; // 垂直采样因子
float* Q_table; // 指向该8×8块使用的量化表
struct huffman_table *AC_table; // 指向该块使用的AC Huffman表
struct huffman_table *DC_table; // 指向该块使用的DC Huffman表
short int previous_DC; // 前一个块的直流DCT系数
short int DCT[64]; // DCT系数数组
#if SANITY_CHECK
unsigned int cid;
#endif
};
struct jdec_private:JPEG数据流结构体,用于存储JPEG图像宽高、数据流指针、Huffman码表等内容,并包含struct huffman_table和struct component。
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文件中包含了很多程序运行过程中的中间变量和解析的情况,便于debug
TRACE设为1,表示其被打开,若设为0,则表示TRACE被关闭
执行程序后,得到的trace_jpeg.txt文件:
3.以txt文件输出所有的量化矩阵和所有的HUFFMAN码表
在tinyjpeg.h中添加声明:
FILE* qtabFilePtr;
FILE* dcImgFilePtr; //DC图像文件
FILE* acImgFilePtr; //AC图像文件
在tinyjpeg.c中添加:
fprintf(qtabFilePtr, "%-6d", ref_table[*zz]);
if (j == 7) {
fprintf(qtabFilePtr, "\n");
}
fprintf(qtabFilePtr, "\n\n");
fprintf(qtabFilePtr, "Quantisation table [%d]:\n", qi); // 量化表ID(added by S.Z.Zheng)
build_quantization_table(table, stream);
在loadjpeg.c中添加
const char* qtabFileName = "q_table.txt"; // 量化表文件名
fopen_s(&qtabFilePtr, qtabFileName, "wb"); // 打开文件
fclose(qtabFilePtr);
输出量化矩阵:
输出HUFFMAN码表:
4.输出DC图像以及某一个AC值图像并统计其概率分布
在tinyjpeg.h中加入
FILE* qtabFilePtr;
FILE* dcImgFilePtr;
FILE* acImgFilePtr;
在tinyjpeg.c中加入
unsigned char* dcImgBuff;
unsigned char* acImgBuff;
unsigned char* uvBuff = 128;
int count = 0;
在loadjpeg.c中添加:
const char* qtabFileName = "quantization_table.txt";
const char* dcImgFileName = "test_decoded_dc.yuv"; // DC图像文件名
const char* acImgFileName = "test_decoded_ac.yuv"; // AC图像文件名
...
fopen_s(&qtabFilePtr, qtabFileName, "wb");
fopen_s(&dcImgFilePtr, dcImgFileName, "wb"); // 打开DC图像文件
fopen_s(&acImgFilePtr, acImgFileName, "wb"); // 打开AC图像文件
...
fclose(qtabFilePtr);
fclose(dcImgFilePtr);
fclose(acImgFilePtr);
得到图像
(但不知道为什么打开显示是这样的:(
ac图像概率分布:
dc图像概率分布: