数据压缩实验五:JPEG原理分析及JPEG解码器的调试
一、JPEG原理分析
编码部分
1.零偏置(Level Offset)
对于灰度级是的像素,通过减去 ,将无符号的整数值变成有符号数。
例:对于 n=8 ,即将 0~255 的值域,通过减去 128 ,转换为值域在[128 ~127] 内。
2.DCT变换
DCT变换的全称是离散余弦变换(DCT),能够将空域的信号转换到频域上。
图像的存储过程中,高频部分往往占用了很大的空间,而低频部分却占用的不多,因此,在图像处理的时候,牺牲部分高频,既可以提高利用率,也能保存下图像的大部分细节。
DCT变换本身是无损的且具有对称性。对原始图像进行离散余弦变换,变换后DCT系数能量主要集中在左上角,其余大部分系数接近于零。
3.量化
uniform scalar quantization
采用中平型均匀量化器
θ(k):DCT系数
Q(k):量化步长
量化表的确定:
因为人眼对亮度信号比对色差信号更敏感,因此使用了两
种量化表:亮度量化值和色差量化值
根据人眼的视觉特性(对低频敏感,对高频不太敏感)对
低频分量采取较细的量化,对高频分量采取较粗的量化
建议基本量化表:
4.量化后系数的编码
1)DC系数差分编码
8 × 8 图像块经过 DCT 变换之后得到的 DC 直流系数有两个特点:
a.系数的数值比较大
b.相邻 8 × 8 图像块的 DC 系数值变化不大:冗余
根据这个特点, JPEG 算法使用了差分脉冲调制编码( DPCM技术),对相邻图像块之间量化 DC 系数的差值 DIFF进行编码:
对差值DIFF进行Huffman编码
2)AC系数
a.由于经 DCT 变换后,系数大多数集中在左上角,即低频
分量区,因此采用 Z 字形按频率的高低顺序读出,可以出
现很多连零的机会。使用游程编码。尤其在最后,如果都是零,给出 EOB (End of Block) 即可。
b.AC系数编码
游程编码
在 JPEG 和 MPEG 编码中规定为:(run,level)
表示连续 run 个 0 ,后面跟值为 level 的系数。
如:−3 0 −3 −2 −6 2 −4 1 −4 1 1 5 1 2 −1 1 −1 2 0 0 0 0 0 −1 −1
EOB
表示为 (0,3) ; (1,-3) ; (0,-2) ; (0,-6) ; (0,2) ; (0,-4) ; (0,1) ; (0,-4) ;(0,1) (0,1) ; (0,5) ; ( 0,1);(0,2) ; (0,-1) ; (0,1) ; (0,-1) ; (0,2) ; (5,-1) ;(0,-1) ; EOB
Run: 最多 15 个,用 4 位表示 Z
Level :类似 DC
- 分成 16 个类别,用 4 位表示表示类别号 C
- 类内索引
对 (Z, C)联合用 Huffman 编码
对类内索引用定长码编码
解码部分
与编码相反:
解码 Huffman 数据
解码 DC 差值
重构量化后的系数
DCT 逆变换
丢弃填充的行 列
反 0 偏置
对丢失的 CbCr 分量差值(下采样的逆过程)
YCbCr ——> RGB
二、JPEG文件格式
JPEG文件大体上可以分成两个部分:标记码(Tag)和压缩数据。
标记码由两个字节构成,前一个字节是固定值0xFF ,后一个字节则根据不同意义有不同数值。
在每个标记码之前还可以添加数目不限的无意义的0xFF填充,也就说连续的多个0xFF可以被理解为一个θxFF ,并表示一个标记码的开始。
在一个完整的两字节的标记码后,就是该标记码对应的压缩数据流,记录了关于文件的诸种信息。
1.图像起始SOI和EOI
一定以 0xFFD8 开始,表示图像开始 SOI (Start of Image) 标记
一定以 0xFFD9 结束,表示图像结束 EOI(End of Image) 标记
2.APP0
固定值0xFFE0
名称 | 字节数 | 描述 |
---|---|---|
APPO长度(length) | 2 | 9个字段的总长度,即不包括标记代码,但包括本字段 |
标识符(identifier) | 5 | 固定值 0x4A46494600,即字符串“JFIF0” |
版本号(version) | 2 | 一般是 0x0102,表示 JFIF 的版本号 1.2,可能会有其他数值代表其他版本 |
X和Y的密度单位 | 1 | units=0: 无单位; units=1: 点数/英寸; units=2:点数/厘米 |
X方向像素密度(X density) | 2 | |
Y方向像素密度(Y density) | 2 | |
缩略图水平像素数目(thumbnail horizontal pixels) | 1 | |
缩略图垂直像素数目(thumbnail vertical pixels) | 1 | |
缩略图RGB位图(thumbnail RGB bi tmap) | 3的倍数 | 缩略图 RGB 位图数据 |
3.APPn
n取1-15(任选)
固定值0xFFE1-0XFFFF
名称 | 字节数 | 描述 |
---|---|---|
APPn长度(length) | 2 | 2 个字段的总长度,即不包括标记代码,但包括本字段 |
详细信息(application specific information) | 数据长度-2 |
4.一个或多个量化表DQT
固定值0xFFDB
名称 | 字节数 | 描述 |
---|---|---|
数据长度(length) | 2 | 2 个字段的总长度,即不包括标记代码,但包括本字段 |
量化表(quantization table) | 数据长度-2 | 精度及量化表id(1字节)高 4 位:精度,只有两个可选值 0:8位;1:16位; 低 4 位:量化表 ID,取值范围为 0~3 |
表项(16*(精度+1)字节) |
5.SOF帧开始
固定值0xFFC0
名称 | 字节数 | 描述 |
---|---|---|
帧开始长度(start of frame length) | 2 | 六个字段的总长度,即不包括标记代码,但包括本字段 |
精度(precision) | 1 | 每个数据样本的位数,通常是 8 位,一般软件都不支持 12 位和 16 位 |
图像高度(image height) | 2 | 图像高度(单位:像素),如果不支持 DNL 就必须 > 0 |
图像宽度(image width) | 2 | 图像宽度(单位:像素),如果不支持 DNL 就必须 > 0 |
颜色分量数(number of color components) | 1 | 只有 3 个数值可选 1:灰度图;3:YCrCb 或 YIQ;4:CMYK; 而 JFIF 中使用 YCrCb,故这里颜色分量数恒为 3 |
对每个颜色分量(for each component) | 颜色分量数*3 | 《颜色分量信息表格》 |
对颜色分量:
- ID
- 垂直方向的样本因子(vertical sample factor)
- 水平方向的样本因子(hori zontal sample factor)
- 量化表号(quantization table#)
6.DHT霍夫曼表
固定值0xFFC4
7.SOS扫描开始
固定值0xFFDA
三、JPEG解码器调试
1.逐步调试JPEG解码器程序。将输入的JPG文件进行解码,将输出文件保存为可供YUVViewer观看的YUV文件。
2. 程序调试过程中,应做到:
理解程序设计的整体框架
loadjpeg.c
主要部分main、load_multiple_times和convert_one_image
以及不同的写出文件(tga、yuv、pgm)的函数
main函数
int main(int argc, char *argv[])
{
int output_format = TINYJPEG_FMT_YUV420P;
char *output_filename, *input_filename;
clock_t start_time, finish_time;
unsigned int duration;
int current_argument;
int benchmark_mode = 0;
if (argc < 3)//如果输入的参数小于3,显示使用指南
usage();
current_argument = 1;//目前进行到的argument
while (1)
{
if (strcmp(argv[current_argument], "--benchmark")==0)//调试界面中的可选项[option]
benchmark_mode = 1;
else
break;
current_argument++;//指向下一串字符
}
if (argc < current_argument+2)//参数不合要求
usage();//再次显示使用指南
input_filename = argv[current_argument];//输入文件名
//if判断要求哪种format,选择不同的输出形式
if (strcmp(argv[current_argument+1],"yuv420p")==0)
output_format = TINYJPEG_FMT_YUV420P;
else if (strcmp(argv[current_argument+1],"rgb24")==0)
output_format = TINYJPEG_FMT_RGB24;
else if (strcmp(argv[current_argument+1],"bgr24")==0)
output_format = TINYJPEG_FMT_BGR24;
else if (strcmp(argv[current_argument+1],"grey")==0)
output_format = TINYJPEG_FMT_GREY;
else
exitmessage("Bad format: need to be one of yuv420p, rgb24, bgr24, grey\n");//format错误
output_filename = argv[current_argument+2];//输出文件名
start_time = clock();
if (benchmark_mode)
load_multiple_times(input_filename, output_filename, output_format);
else
convert_one_image(input_filename, output_filename, output_format);
finish_time = clock();
duration = finish_time - start_time;
snprintf(error_string, sizeof(error_string),"Decoding finished in %u ticks\n", duration);
#if TRACE
fclose(p_trace);
#endif
return 0;
}
load_multiple_times
/**
* Load one jpeg image, and try to decompress 1000 times, and save the result.
* This is mainly used for benchmarking the decoder, or to test if between each
* called of the library the DCT is corrected reset (a bug was found).
*/
int load_multiple_times(const char *filename, const char *outfilename, int output_format)
{
FILE *fp;
int cout, length_of_file;//定义计数和输入文件长度
unsigned int width, height;//定义宽和高
unsigned char *buf;//定义一个buf的缓冲区
struct jdec_private *jdec;//定义一个jdec_private类型的jdec
unsigned char *components[4];//定义指针数组
jdec = tinyjpeg_init();
count = 0;
/* Load the Jpeg into memory */
fp = fopen(filename, "rb");
if (fp == NULL)
exitmessage("Cannot open filename\n");
length_of_file = filesize(fp);
buf = (unsigned char *)malloc(length_of_file + 4);
fread(buf, length_of_file, 1, fp);
fclose(fp);
while (count<1000)//图像解码1000次
{
if (tinyjpeg_parse_header(jdec, buf, length_of_file)<0)
exitmessage(tinyjpeg_get_errorstring(jdec));
tinyjpeg_decode(jdec, output_format);
count++;
}
/*
* Get address for each plane (not only max 3 planes is supported), and
* depending of the output mode, only some components will be filled
* RGB: 1 plane, YUV420P: 3 planes, GREY: 1 plane
*/
tinyjpeg_get_components(jdec, components);
tinyjpeg_get_size(jdec, &width, &height);
/* Save it */
switch (output_format)
{
case TINYJPEG_FMT_RGB24:
case TINYJPEG_FMT_BGR24:
write_tga(outfilename, output_format, width, height, components);
break;
case TINYJPEG_FMT_YUV420P:
write_yuv(outfilename, width, height, components);
break;
case TINYJPEG_FMT_GREY:
write_pgm(outfilename, width, height, components);
break;
}
free(buf);
tinyjpeg_free(jdec);
return 0;
}
convert_one_image
int convert_one_image(const char *infilename, const char *outfilename, int output_format)
{
FILE *fp;
unsigned int length_of_file;//定义输入文件长度
unsigned int width, height;//定义宽和高
unsigned char *buf;//定义一个buf的缓冲区
struct jdec_private *jdec;//定义一个jdec_private类型的jdec
unsigned char *components[3];//定义指针数组
/* Load the Jpeg into memory */
//打开文件之后把图片的基本信息写入
fp = fopen(infilename, "rb");
if (fp == NULL)
exitmessage("Cannot open filename\n");
length_of_file = filesize(fp);
buf = (unsigned char *)malloc(length_of_file + 4);
if (buf == NULL)
exitmessage("Not enough memory for loading file\n");
fread(buf, length_of_file, 1, fp);
fclose(fp);
/* Decompress it */
jdec = tinyjpeg_init();//初始化
if (jdec == NULL)
exitmessage("Not enough memory to alloc the structure need for decompressing\n");
if (tinyjpeg_parse_header(jdec, buf, length_of_file)<0)//解压缩JPEG图像的头信息
exitmessage(tinyjpeg_get_errorstring(jdec));
/* Get the size of the image */
tinyjpeg_get_size(jdec, &width, &height);
DCT_0_mat = (short int*)malloc(width * height / 64 * sizeof(short int));
DCT_1_mat = (short int*)malloc(width * height / 64 * sizeof(short int));
DC_pos = 0;
AC_pos = 0;
snprintf(error_string, sizeof(error_string),"Decoding JPEG image...\n");
if (tinyjpeg_decode(jdec, output_format) < 0)
exitmessage(tinyjpeg_get_errorstring(jdec));
/*
* Get address for each plane (not only max 3 planes is supported), and
* depending of the output mode, only some components will be filled
* RGB: 1 plane, YUV420P: 3 planes, GREY: 1 plane
*/
tinyjpeg_get_components(jdec, components);
/* Save it */ //写输出文件
switch (output_format)
{
case TINYJPEG_FMT_RGB24:
case TINYJPEG_FMT_BGR24:
write_tga(outfilename, output_format, width, height, components);
break;
case TINYJPEG_FMT_YUV420P:
write_yuv(outfilename, width, height, components);
write_DC_AC(width, height);
break;
case TINYJPEG_FMT_GREY:
write_pgm(outfilename, width, height, components);
break;
}
/* Only called this if the buffers were allocated by tinyjpeg_decode() */
tinyjpeg_free(jdec);
/* else called just free(jdec); */
free(buf);
return 0;
}
tinyjpeg.c
1.4个函数处理数据流
fill_ nbits:数据放入存储库。将任何0xff,0x00转换为0xff。
get_ nbits:从流中读取nbits,并写入结果,nbits从流中移除,寄存器被自动填满。
look_ nbits:从流中读取nbits,而不将其标记为已读。
Skip_nbits:从流中读取nbits,但不返回结果。
流:当前指针在JPEG数据(每字节读取字节)
reservoir:包含信息位的寄存器。只有nbits_ in_ reservoir
有效。
#define fill_nbits(reservoir,nbits_in_reservoir,stream,nbits_wanted) do { \
while (nbits_in_reservoir<nbits_wanted) \
{ \
unsigned char c; \
if (stream >= priv->stream_end) \
longjmp(priv->jump_state, -EIO); \
c = *stream++; \
reservoir <<= 8; \
if (c == 0xff && *stream == 0x00) \
stream++; \
reservoir |= c; \
nbits_in_reservoir+=8; \
} \
} while(0);
/* Signed version !!!! */
#define get_nbits(reservoir,nbits_in_reservoir,stream,nbits_wanted,result) do { \
fill_nbits(reservoir,nbits_in_reservoir,stream,(nbits_wanted)); \
result = ((reservoir)>>(nbits_in_reservoir-(nbits_wanted))); \
nbits_in_reservoir -= (nbits_wanted); \
reservoir &= ((1U<<nbits_in_reservoir)-1); \
if ((unsigned int)result < (1UL<<((nbits_wanted)-1))) \
result += (0xFFFFFFFFUL<<(nbits_wanted))+1; \
} while(0);
#define look_nbits(reservoir,nbits_in_reservoir,stream,nbits_wanted,result) do { \
fill_nbits(reservoir,nbits_in_reservoir,stream,(nbits_wanted)); \
result = ((reservoir)>>(nbits_in_reservoir-(nbits_wanted))); \
} while(0);
/* To speed up the decoding, we assume that the reservoir have enough bit
* slow version:
* #define skip_nbits(reservoir,nbits_in_reservoir,stream,nbits_wanted) do { \
* fill_nbits(reservoir,nbits_in_reservoir,stream,(nbits_wanted)); \
* nbits_in_reservoir -= (nbits_wanted); \
* reservoir &= ((1U<<nbits_in_reservoir)-1); \
* } while(0);
*/
#define skip_nbits(reservoir,nbits_in_reservoir,stream,nbits_wanted) do { \
nbits_in_reservoir -= (nbits_wanted); \
reservoir &= ((1U<<nbits_in_reservoir)-1); \
} while(0);
2.创建霍夫曼表
/**
*
* Decode a single block that contains the DCT coefficients.
* The table coefficients is already dezigzaged at the end of the operation.
*
*/
static void process_Huffman_data_unit(struct jdec_private *priv, int component)
{
unsigned char j;
unsigned int huff_code;
unsigned char size_val, count_0;
struct component *c = &priv->component_infos[component];
short int DCT[64];
/* Initialize the DCT coef table */
memset(DCT, 0, sizeof(DCT));
/* DC coefficient decoding */
huff_code = get_next_huffman_code(priv, c->DC_table);
//trace("+ %x\n", huff_code);
if (huff_code) {
get_nbits(priv->reservoir, priv->nbits_in_reservoir, priv->stream, huff_code, DCT[0]);
DCT[0] += c->previous_DC;
c->previous_DC = DCT[0];
} else {
DCT[0] = c->previous_DC;
}
/* AC coefficient decoding */
j = 1;
while (j<64)
{
huff_code = get_next_huffman_code(priv, c->AC_table);
//trace("- %x\n", huff_code);
size_val = huff_code & 0xF;
count_0 = huff_code >> 4;
if (size_val == 0)
{ /* RLE */
if (count_0 == 0)
break; /* EOB found, go out */
else if (count_0 == 0xF)
j += 16; /* skip 16 zeros */
}
else
{
j += count_0; /* skip count_0 zeroes */
if (__unlikely(j >= 64))
{
snprintf(error_string, sizeof(error_string), "Bad huffman data (buffer overflow)");
break;
}
get_nbits(priv->reservoir, priv->nbits_in_reservoir, priv->stream, size_val, DCT[j]);
j++;
}
}
for (j = 0; j < 64; j++)
c->DCT[j] = DCT[zigzag[j]];
}
/*
* Takes two array of bits, and build the huffman table for size, and code
*
* lookup will return the symbol if the code is less or equal than HUFFMAN_HASH_NBITS.
* code_size will be used to known how many bits this symbol is encoded.
* slowtable will be used when the first lookup didn't give the result.
*/
static void build_huffman_table(const unsigned char *bits, const unsigned char *vals, struct huffman_table *table)
{
unsigned int i, j, code, code_size, val, nbits;
unsigned char huffsize[HUFFMAN_BITS_SIZE+1], *hz;
unsigned int huffcode[HUFFMAN_BITS_SIZE+1], *hc;
int next_free_entry;
/*
* Build a temp array
* huffsize[X] => numbers of bits to write vals[X]
*/
hz = huffsize;
for (i=1; i<=16; i++)
{
for (j=1; j<=bits[i]; j++)
*hz++ = i;
}
*hz = 0;
memset(table->lookup, 0xff, sizeof(table->lookup));
for (i=0; i<(16-HUFFMAN_HASH_NBITS); i++)
table->slowtable[i][0] = 0;
/* Build a temp array
* huffcode[X] => code used to write vals[X]
*/
code = 0;
hc = huffcode;
hz = huffsize;
nbits = *hz;
while (*hz)
{
while (*hz == nbits)
{
*hc++ = code++;
hz++;
}
code <<= 1;
nbits++;
}
/*
* Build the lookup table, and the slowtable if needed.
*/
next_free_entry = -1;
for (i=0; huffsize[i]; i++)
{
val = vals[i];
code = huffcode[i];
code_size = huffsize[i];
#if TRACE
fprintf(p_trace,"val=%2.2x code=%8.8x codesize=%2.2d\n", val, code, code_size);
fflush(p_trace);
#endif
table->code_size[val] = code_size;
if (code_size <= HUFFMAN_HASH_NBITS)
{
/*
* Good: val can be put in the lookup table, so fill all value of this
* column with value val
*/
int repeat = 1UL<<(HUFFMAN_HASH_NBITS - code_size);
code <<= HUFFMAN_HASH_NBITS - code_size;
while ( repeat-- )
table->lookup[code++] = val;
}
else
{
/* Perhaps sorting the array will be an optimization */
uint16_t *slowtable = table->slowtable[code_size-HUFFMAN_HASH_NBITS-1];
while(slowtable[0])
slowtable+=2;
slowtable[0] = code;
slowtable[1] = val;
slowtable[2] = 0;
/* TODO: NEED TO CHECK FOR AN OVERFLOW OF THE TABLE */
}
}
}
static void build_default_huffman_tables(struct jdec_private *priv)
{
if ( (priv->flags & TINYJPEG_FLAGS_MJPEG_TABLE)
&& priv->default_huffman_table_initialized)
return;
build_huffman_table(bits_dc_luminance, val_dc_luminance, &priv->HTDC[0]);
build_huffman_table(bits_ac_luminance, val_ac_luminance, &priv->HTAC[0]);
build_huffman_table(bits_dc_chrominance, val_dc_chrominance, &priv->HTDC[1]);
build_huffman_table(bits_ac_chrominance, val_ac_chrominance, &priv->HTAC[1]);
priv->default_huffman_table_initialized = 1;
}
3.建立量化表
static void build_quantization_table(float *qtable, const unsigned char *ref_table)
{
/* Taken from libjpeg. Copyright Independent JPEG Group's LLM idct.
* For float AA&N IDCT method, divisors are equal to quantization
* coefficients scaled by scalefactor[row]*scalefactor[col], where
* scalefactor[0] = 1
* scalefactor[k] = cos(k*PI/16) * sqrt(2) for k=1..7
* We apply a further scale factor of 8.
* What's actually stored is 1/divisor so that the inner loop can
* use a multiplication rather than a division.
*/
int i, j;
static const double aanscalefactor[8] = {
1.0, 1.387039845, 1.306562965, 1.175875602,
1.0, 0.785694958, 0.541196100, 0.275899379
};
const unsigned char *zz = zigzag;
for (i=0; i<8; i++) {
for (j=0; j<8; j++) {
*qtable++ = ref_table[*zz++] * aanscalefactor[i] * aanscalefactor[j];
}
}
}
4.解析DQT
static int parse_DQT(struct jdec_private *priv, const unsigned char *stream)
{
int qi;
float *table;
const unsigned char *dqt_block_end;
#if TRACE
fprintf(p_trace,"> DQT marker\n");
fflush(p_trace);
#endif
dqt_block_end = stream + be16_to_cpu(stream);
stream += 2; /* Skip length */
while (stream < dqt_block_end)
{
qi = *stream++;
#if SANITY_CHECK
if (qi>>4)
snprintf(error_string, sizeof(error_string),"16 bits quantization table is not supported\n");
if (qi>4)
snprintf(error_string, sizeof(error_string),"No more 4 quantization table is supported (got %d)\n", qi);
#endif
table = priv->Q_tables[qi];
build_quantization_table(table, stream);
stream += 64;
}
#if TRACE
fprintf(p_trace,"< DQT marker\n");
fflush(p_trace);
#endif
return 0;
}
5.解析SOF
static int parse_SOF(struct jdec_private *priv, const unsigned char *stream)
{
int i, width, height, nr_components, cid, sampling_factor;
int Q_table;
struct component *c;
#if TRACE
fprintf(p_trace,"> SOF marker\n");
fflush(p_trace);
#endif
print_SOF(stream);
height = be16_to_cpu(stream+3);
width = be16_to_cpu(stream+5);
nr_components = stream[7];
#if SANITY_CHECK
if (stream[2] != 8)
snprintf(error_string, sizeof(error_string),"Precision other than 8 is not supported\n");
if (width>JPEG_MAX_WIDTH || height>JPEG_MAX_HEIGHT)
snprintf(error_string, sizeof(error_string),"Width and Height (%dx%d) seems suspicious\n", width, height);
if (nr_components != 3)
snprintf(error_string, sizeof(error_string),"We only support YUV images\n");
if (height%16)
snprintf(error_string, sizeof(error_string),"Height need to be a multiple of 16 (current height is %d)\n", height);
if (width%16)
snprintf(error_string, sizeof(error_string),"Width need to be a multiple of 16 (current Width is %d)\n", width);
#endif
stream += 8;
for (i=0; i<nr_components; i++) {
cid = *stream++;
sampling_factor = *stream++;
Q_table = *stream++;
c = &priv->component_infos[i];
#if SANITY_CHECK
c->cid = cid;
if (Q_table >= COMPONENTS)
snprintf(error_string, sizeof(error_string),"Bad Quantization table index (got %d, max allowed %d)\n", Q_table, COMPONENTS-1);
#endif
c->Vfactor = sampling_factor&0xf;
c->Hfactor = sampling_factor>>4;
c->Q_table = priv->Q_tables[Q_table];
#if TRACE
fprintf(p_trace,"Component:%d factor:%dx%d Quantization table:%d\n",
cid, c->Hfactor, c->Hfactor, Q_table );
fflush(p_trace);
#endif
}
priv->width = width;
priv->height = height;
#if TRACE
fprintf(p_trace,"< SOF marker\n");
fflush(p_trace);
#endif
return 0;
}
6.解析SOS
static int parse_SOS(struct jdec_private *priv, const unsigned char *stream)
{
unsigned int i, cid, table;
unsigned int nr_components = stream[2];
#if TRACE
fprintf(p_trace,"> SOS marker\n");
fflush(p_trace);
#endif
#if SANITY_CHECK
if (nr_components != 3)
snprintf(error_string, sizeof(error_string),"We only support YCbCr image\n");
#endif
stream += 3;
for (i=0;i<nr_components;i++) {
cid = *stream++;
table = *stream++;
#if SANITY_CHECK
if ((table&0xf)>=4)
snprintf(error_string, sizeof(error_string),"We do not support more than 2 AC Huffman table\n");
if ((table>>4)>=4)
snprintf(error_string, sizeof(error_string),"We do not support more than 2 DC Huffman table\n");
if (cid != priv->component_infos[i].cid)
snprintf(error_string, sizeof(error_string),"SOS cid order (%d:%d) isn't compatible with the SOF marker (%d:%d)\n",
i, cid, i, priv->component_infos[i].cid);
#if TRACE
fprintf(p_trace,"ComponentId:%d tableAC:%d tableDC:%d\n", cid, table&0xf, table>>4);
fflush(p_trace);
#endif
#endif
priv->component_infos[i].AC_table = &priv->HTAC[table&0xf];
priv->component_infos[i].DC_table = &priv->HTDC[table>>4];
}
priv->stream = stream+3;
#if TRACE
fprintf(p_trace,"< SOS marker\n");
fflush(p_trace);
#endif
return 0;
}
7.解析DHT
static int parse_DHT(struct jdec_private *priv, const unsigned char *stream)
{
unsigned int count, i;
unsigned char huff_bits[17];
int length, index;
length = be16_to_cpu(stream) - 2;
stream += 2; /* Skip length */
#if TRACE
fprintf(p_trace,"> DHT marker (length=%d)\n", length);
fflush(p_trace);
#endif
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);
#endif
#endif
if (index & 0xf0 )
build_huffman_table(huff_bits, stream, &priv->HTAC[index&0xf]);
else
build_huffman_table(huff_bits, stream, &priv->HTDC[index&0xf]);
length -= 1;
length -= 16;
length -= count;
stream += count;
}
#if TRACE
fprintf(p_trace,"< DHT marker\n");
fflush(p_trace);
#endif
return 0;
}
8.解析DRI
static int parse_DRI(struct jdec_private *priv, const unsigned char *stream)
{
unsigned int length;
#if TRACE
fprintf(p_trace,"> DRI marker\n");
fflush(p_trace);
#endif
length = be16_to_cpu(stream);
#if SANITY_CHECK
if (length != 4)
snprintf(error_string, sizeof(error_string),"Length of DRI marker need to be 4\n");
#endif
priv->restart_interval = be16_to_cpu(stream+2);
#if TRACE
fprintf(p_trace,"Restart interval = %d\n", priv->restart_interval);
fprintf(p_trace,"< DRI marker\n");
fflush(p_trace);
#endif
return 0;
}
9.parse_JFIF
完成标记解析
static int parse_JFIF(struct jdec_private *priv, const unsigned char *stream)
{
int chuck_len;
int marker;
int sos_marker_found = 0;
int dht_marker_found = 0;
const unsigned char *next_chunck;
/* Parse marker */
while (!sos_marker_found)
{
if (*stream++ != 0xff)
goto bogus_jpeg_format;
/* Skip any padding ff byte (this is normal) */
while (*stream == 0xff)
stream++;
marker = *stream++;
chuck_len = be16_to_cpu(stream);
next_chunck = stream + chuck_len;
switch (marker)
{
case SOF:
if (parse_SOF(priv, stream) < 0)
return -1;
break;
case DQT:
if (parse_DQT(priv, stream) < 0)
return -1;
break;
case SOS:
if (parse_SOS(priv, stream) < 0)
return -1;
sos_marker_found = 1;
break;
case DHT:
if (parse_DHT(priv, stream) < 0)
return -1;
dht_marker_found = 1;
break;
case DRI:
if (parse_DRI(priv, stream) < 0)
return -1;
break;
default:
#if TRACE
fprintf(p_trace,"> Unknown marker %2.2x\n", marker);
fflush(p_trace);
#endif
break;
}
stream = next_chunck;
}
if (!dht_marker_found) {
#if TRACE
fprintf(p_trace,"No Huffman table loaded, using the default one\n");
fflush(p_trace);
#endif
build_default_huffman_tables(priv);
}
#ifdef SANITY_CHECK
if ( (priv->component_infos[cY].Hfactor < priv->component_infos[cCb].Hfactor)
|| (priv->component_infos[cY].Hfactor < priv->component_infos[cCr].Hfactor))
snprintf(error_string, sizeof(error_string),"Horizontal sampling factor for Y should be greater than horitontal sampling factor for Cb or Cr\n");
if ( (priv->component_infos[cY].Vfactor < priv->component_infos[cCb].Vfactor)
|| (priv->component_infos[cY].Vfactor < priv->component_infos[cCr].Vfactor))
snprintf(error_string, sizeof(error_string),"Vertical sampling factor for Y should be greater than vertical sampling factor for Cb or Cr\n");
if ( (priv->component_infos[cCb].Hfactor!=1)
|| (priv->component_infos[cCr].Hfactor!=1)
|| (priv->component_infos[cCb].Vfactor!=1)
|| (priv->component_infos[cCr].Vfactor!=1))
snprintf(error_string, sizeof(error_string),"Sampling other than 1x1 for Cr and Cb is not supported");
#endif
return 0;
bogus_jpeg_format:
#if TRACE
fprintf(p_trace,"Bogus jpeg format\n");
fflush(p_trace);
#endif
return -1;
}
10.解析文件头
初始化准备
int tinyjpeg_parse_header(struct jdec_private *priv, const unsigned char *buf, unsigned int size)
{
int ret;
/* Identify the file */
if ((buf[0] != 0xFF) || (buf[1] != SOI))
snprintf(error_string, sizeof(error_string),"Not a JPG file ?\n");
priv->stream_begin = buf+2;
priv->stream_length = size-2;
priv->stream_end = priv->stream_begin + priv->stream_length;
ret = parse_JFIF(priv, priv->stream_begin);
return ret;
}
11.指向宏块解码
typedef void (*decode_MCU_fct) (struct jdec_private *priv);
static const decode_MCU_fct decode_mcu_3comp_table[4] = {
decode_MCU_1x1_3planes,
decode_MCU_1x2_3planes,
decode_MCU_2x1_3planes,
decode_MCU_2x2_3planes,
};
static const decode_MCU_fct decode_mcu_1comp_table[4] = {
decode_MCU_1x1_1plane,
decode_MCU_1x2_1plane,
decode_MCU_2x1_1plane,
decode_MCU_2x2_1plane,
};
12.指向彩色空间转换
typedef void (*convert_colorspace_fct) (struct jdec_private *priv);
static const convert_colorspace_fct convert_colorspace_yuv420p[4] = {
YCrCB_to_YUV420P_1x1,
YCrCB_to_YUV420P_1x2,
YCrCB_to_YUV420P_2x1,
YCrCB_to_YUV420P_2x2,
};
static const convert_colorspace_fct convert_colorspace_rgb24[4] = {
YCrCB_to_RGB24_1x1,
YCrCB_to_RGB24_1x2,
YCrCB_to_RGB24_2x1,
YCrCB_to_RGB24_2x2,
};
static const convert_colorspace_fct convert_colorspace_bgr24[4] = {
YCrCB_to_BGR24_1x1,
YCrCB_to_BGR24_1x2,
YCrCB_to_BGR24_2x1,
YCrCB_to_BGR24_2x2,
};
static const convert_colorspace_fct convert_colorspace_grey[4] = {
YCrCB_to_Grey_1x1,
YCrCB_to_Grey_1x2,
YCrCB_to_Grey_2x1,
YCrCB_to_Grey_2x2,
};
理解三个结构体的设计目的
• struct huffman_table
快速查找表
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
保存宏块的信息
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,打开,进行上述说明的信息的输出
将TRACE的值置为0,关闭,跳过。
#if TRACE
//添加希望看到的信息代码
#endif
3.以txt文件输出所有的量化矩阵和所有的HUFFMAN码表。
4. 输出DC图像并统计其概率分布。
5. 输出某一个AC值图像并统计其概率分布。
上一篇: 数据压缩原理 实验五 JPEG原理分析及JPEG解码器的调试
下一篇: 数据压缩第十二次作业