JPEG是国际标准化组织制定的一种压缩标准,用于图像,后缀为.jpg或.jpeg
JPEG 在文件中以Segment 的形式组织,它具有以下特点:
1 在标记段DHT内,包含了一个或者多个的哈夫曼表。对于单一个哈夫曼表,应该包括了三部分:
依据每个分量的水平、垂直采样因子计算 MCU 的大小,并得到每个 MCU 中 8*8
宏块的个数
对每个 MCU 解码(依照各分量水平、垂直采样因子对 MCU 中每个分量宏块解码)
主函数如下
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 TRACE
p_trace=fopen(TRACEFILE,"w");
if (p_trace==NULL)
{
printf("trace file open error!");
}
#endif//当trace为真时运行上面的if
if (argc < 3)
usage();
current_argument = 1;
while (1)
{
if (strcmp(argv[current_argument], "--benchmark")==0)
benchmark_mode = 1;
else
break;
current_argument++;
}
if (argc < current_argument+2)
usage();
input_filename = argv[current_argument];
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");
output_filename = argv[current_argument+2];
start_time = clock();
if (benchmark_mode)
load_multiple_times(input_filename, output_filename, output_format);
else//不是benchmarkmode的时候,运行下面这行代码
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;
}
不难看出,convert_one_image这个函数起到了关键的作用,代码如下
/*
* Load one jpeg image, and decompress it, and save the result.
*/
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;
struct 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");
//对parseheader进行解码
if (tinyjpeg_parse_header(jdec, buf, length_of_file)<0)
exitmessage(tinyjpeg_get_errorstring(jdec));
/* Get the size of the image */
tinyjpeg_get_size(jdec, &width, &height);//获取图像的大小
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);
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;
}
建立一个便于查找的快速查找表,快表里找不到就用慢表
{
/* 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 */
* 快速查找表,利用HUFFMAN_HASH_NBITS我们可以直接找到符号,如果符号小于0,我们就需要取树表里找了
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];
};
用来储存每一个8*8块的信息,会不断更新,但是会保留一些数据便于解码
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 */上以块的dc系数
short int DCT[64]; /* DCT coef */dct系数组
#if SANITY_CHECK
unsigned int cid;
#endif
};
这个结构体在程序中的使用相当多,大部分函数里都有它的影子,它起到的作用是储存一些图像信息和表
struct jdec_private
{
/* Public variables */
uint8_t *components[COMPONENTS];//yuv分量的数组
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 */dc系数码表
struct huffman_table HTAC[HUFFMAN_TABLES]; /* AC huffman tables */ac系数码表
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];
};
write_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, F);
fwrite(components[1], width / 2, height / 2, F);
fwrite(components[2], width / 2, height / 2, F);
fclose(F);
}
这次用的图像如下,特别酷炫
我们输出的yuv文件预览如下,怎么调都显示不全[捂脸]
首先定义trace为1
其中的内容会生成 到trace_jpeg.txt.中
可以看到,这里都是一些较为重要的信息
对这里的输出进行了一些小的改动,让程序不输出DHTmarker,查看一下结果
可以看到,确实没有输出,trace修改成功
在主函数中添加如下代码,打开几个文件
fopen_s(&codetab, "codetab.txt", "ab");
fopen_s(&DC_ima, "DC_ima.yuv", "ab");
fopen_s(&AC_ima, "AC_ima.yuv", "ab");
fopen_s(&q_mat, "q_mat.txt", "ab");
由于要输出huffman表,在build_huffman_table()中添加如下代码
fprintf(codetab,"val=%2.2x code=%8.8x codesize=%2.2d \n",val,code,code_size);
在parse_DHT中添加
fprintf(codetab,"%s \n",(index&0xf0)?"AC":"DC");
要输出量化表,要在parse_DQT添加
fprintf(q_mat, "quantization_table: \n");
还要在build_quantization_table添加
#if TRACE
if (j == 7) { fprintf(q_mat, "\n"); fflush(q_mat); }
fprintf(q_mat, "%d\t",ref_table[*zz]);
fflush(q_mat);
#endif
输出的txt文件如下,很奇怪,明明有输出\n,但是文件中并没有换行,导致很乱
但其实huffman码表在trace_jpeg.txt里都有(嘿嘿嘿)