JPEG编码过程如上图所示,解码是编码的逆过程。下面以编码为例进行分析。
由于8×8图像块经过DCT变换之后得到的DC直流系数有两个特点:
根据这个特点,JPEG算法使用了差分脉冲调制编码(DPCM)技术,对相邻图像块之间量化DC系数的差值DIFF进行编码:
D I F F k = D C k − D C k − 1 DIFF_k=DC_k-DC_{k-1} DIFFk=DCk−DCk−1
Amplitude:表示非零系数的幅度值;
Run:表示零的游程即零的个数;
Size:表示非零系数的幅度值的编码位数。
JPEG在文件中以Segment的形式组织 ,它具有以下特点:
遵循程序要求,在调试属性内设定好命令参数。
观察调试解码程序发现,Y、U、V分量输出过程在如下所示的函数内,在//输出yuv文件
后增加一段程序输出YUV文件。再次运行程序。
/**
* Save a buffer in three files (.Y, .U, .V) useable by yuvsplittoppm
*/
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);
//输出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);
}
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; //8x8块使用的量化表
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数据流结构体。
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
为活动预处理器块,用来协助程序的调试。在本程序的头文件tinyjpeg.h
中将TRACE
设为1,表示程序正常调试运行,若想关闭,可设为0。
#define TRACE 1//add by nxn
在头文件tinyjpeg.h
中声明要输出的文件:
FILE* qfile;//量化矩阵txt文件
FILE* hfile;//Huffman码表txt文件
build_quantization_table()
中添加量化矩阵输出代码; //输出所有量化矩阵
qfile = fopen("quan.txt", "a");
for (i = 0; i < 8; i++) {
for (j = 0; j < 8; j++) {
fprintf(qfile, "%d\t", ref_table[*zz]);
*qtable++ = ref_table[*zz++] * aanscalefactor[i] * aanscalefactor[j];
}
fprintf(qfile, "\n");
}
parse_DQT()
中添加代码进行量化矩阵文件的更新写入。#if TRACE
fprintf(p_trace, "< DQT marker\n");
fflush(p_trace);
//更新打印量化矩阵
fprintf(qfile, "DQT ID: %d\n", qi);
fflush(qfile);
#endif
build_huffman_table()
内添加代码打印输出Huffman码表。if TRACE
fprintf(p_trace, "val=%2.2x code=%8.8x codesize=%2.2d\n", val, code, code_size);
fflush(p_trace);
fprintf(hfile, "val=%2.2x code=%8.8x codesize=%2.2d\n", val, code, code_size);
fflush(hfile);
#endif
parse_DHT()
中添加代码实现输出Huffman码表的分类(DC或AC及序号)。//输出huffman码表
hfile = fopen("huff.txt", "a");
while (length > 0) {
index = *stream++;
/* We need to calculate the number of bytes 'vals' will takes */
huff_bits[0] = 0;
count = 0;
for (i = 1; i < 17; i++) {
huff_bits[i] = *stream++;
count += huff_bits[i];
}
#if SANITY_CHECK
if (count >= HUFFMAN_BITS_SIZE)
snprintf(error_string, sizeof(error_string), "No more than %d bytes is allowed to describe a huffman table", HUFFMAN_BITS_SIZE);
if ((index & 0xf) >= HUFFMAN_TABLES)
snprintf(error_string, sizeof(error_string), "No more than %d Huffman tables is supported (got %d)\n", HUFFMAN_TABLES, index & 0xf);
#if TRACE
fprintf(p_trace, "Huffman table %s[%d] length=%d\n", (index & 0xf0) ? "AC" : "DC", index & 0xf, count);
fflush(p_trace);
fprintf(hfile, "Huffman table %s[%d] length=%d\n", (index & 0xf0) ? "AC" : "DC", index & 0xf, count);
fflush(hfile);
通过本次实验: