注:分析Tiny Jpeg Decoder源代码的文章:
Tiny Jpeg Decoder (JPEG解码程序) 源代码分析 1:解码文件头
Tiny Jpeg Decoder (JPEG解码程序) 源代码分析 2:解码数据
===================
Tiny Jpeg Decoder是一个可以用于嵌入式系统的JPEG解码器。也可以在Windows上编译通过。在此分析一下它部分的源代码,辅助学习JPEG解码知识。
通过TinyJpeg可以将JPEG(*.jpg)文件解码为YUV(*.yuv)或者RGB(*.tga)文件。
真正的解码开始于convert_one_image()函数:
/** * Load one jpeg image, and decompress it, and save the result. */ int convert_one_image(LPVOID lparam,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(); //传入句柄-------------- jdec->dlg=(CSpecialVIJPGDlg *)lparam; 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) 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: //开始写入成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() */ //modify by lei! tinyjpeg_free(jdec); /* else called just free(jdec); */ free(buf); return 0; }
tinyjpeg_init()用于初始化:
/** * Allocate a new tinyjpeg decoder object. * * Before calling any other functions, an object need to be called. */ struct jdec_private *tinyjpeg_init(void) { struct jdec_private *priv; priv = (struct jdec_private *)calloc(1, sizeof(struct jdec_private)); if (priv == NULL) return NULL; priv->DQT_table_num=0; return priv; }
tinyjpeg_parse_header()用于解码JPEG文件头,可见函数前几句主要验证文件是否为JPEG文件:
/** * Initialize the tinyjpeg object and prepare the decoding of the stream. * * Check if the jpeg can be decoded with this jpeg decoder. * Fill some table used for preprocessing. */ int tinyjpeg_parse_header(struct jdec_private *priv, const unsigned char *buf, unsigned int size) { int ret; /* Identify the file */ //0x FF D8 //是否是JPEG格式文件? if ((buf[0] != 0xFF) || (buf[1] != SOI)) snprintf(error_string, sizeof(error_string),"Not a JPG file ?\n"); //是 char temp_str[MAX_URL_LENGTH]; sprintf(temp_str,"0x %X %X",buf[0],buf[1]); //JPEG格式文件固定的文件头 //begin指针前移2字节 priv->stream_begin = buf+2; priv->stream_length = size-2; priv->stream_end = priv->stream_begin + priv->stream_length; //开始解析JFIF ret = parse_JFIF(priv, priv->stream_begin); return ret; }
parse_JFIF()用于解析各种标签(SOF,SOS,DHT...):
//解各种不同的标签 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 */ //在Start of scan标签之前 while (!sos_marker_found) { if (*stream++ != 0xff) goto bogus_jpeg_format; /* Skip any padding ff byte (this is normal) */ //跳过0xff字节 while (*stream == 0xff) stream++; //marker是跳过0xff字节后1个字节的内容 marker = *stream++; //chunk_len是marker后面2个字节的内容(大端模式需要转换) //包含自身,但不包含0xff+marker2字节 chuck_len = be16_to_cpu(stream); //指向下一个chunk的指针 next_chunck = stream + chuck_len; //各种不同的标签 switch (marker) { case SOF: //开始解析SOF if (parse_SOF(priv, stream) < 0) return -1; break; //Define quantization table case DQT: //开始解析DQT if (parse_DQT(priv, stream) < 0) return -1; break; case SOS: //开始解析SOS if (parse_SOS(priv, stream) < 0) return -1; sos_marker_found = 1; break; //Define Huffman table case DHT: //开始解析DHT if (parse_DHT(priv, stream) < 0) return -1; dht_marker_found = 1; break; case DRI: //开始解析DRI if (parse_DRI(priv, stream) < 0) return -1; break; default: #if TRACE_PARAM fprintf(param_trace,"> Unknown marker %2.2x\n", marker); fflush(param_trace); #endif break; } //解下一个segment stream = next_chunck; } if (!dht_marker_found) { #if TRACE_PARAM fprintf(param_trace,"No Huffman table loaded, using the default one\n"); fflush(param_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_PARAM fprintf(param_trace,"Bogus jpeg format\n"); fflush(param_trace); #endif return -1; }
parse_SOF()用于解析SOF标签:
注意:其中包含了部分自己写的代码,形如:
itoa(width,temp_str1,10); priv->dlg->AppendBInfo("SOF0","宽",temp_str1,"图像的宽度");
这些代码主要用于在解码过程中提取一些信息,比如图像宽,高,颜色分量数等等
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_PARAM fprintf(param_trace,"> SOF marker\n"); fflush(param_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 char temp_str1[MAX_URL_LENGTH]={0}; itoa(width,temp_str1,10); priv->dlg->AppendBInfo("SOF0","宽",temp_str1,"图像的宽度"); itoa(height,temp_str1,10); priv->dlg->AppendBInfo("SOF0","高",temp_str1,"图像的高度"); itoa(nr_components,temp_str1,10); priv->dlg->AppendBInfo("SOF0","颜色分量数",temp_str1,"图像的颜色分量数。一个字节,例如03,代表有三个分量,YCrCb"); itoa(stream[2],temp_str1,10); priv->dlg->AppendBInfo("SOF0","精度",temp_str1,"图像的精度。一个字节,例如08,即精度为一个字节。"); 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]; //------------ char temp_str2[MAX_URL_LENGTH]={0}; sprintf(temp_str2,"垂直采样因子【%d】",i); itoa(c->Hfactor,temp_str1,10); priv->dlg->AppendBInfo("SOF0",temp_str2,temp_str1,"颜色分量信息:每个分量有三个字节,第一个为分量的ID,01:Y 02:U 03:V;第二个字节高位为水平采样因子,低位为垂直采样因子。"); sprintf(temp_str2,"水平采样因子【%d】",i); itoa(c->Hfactor,temp_str1,10); priv->dlg->AppendBInfo("SOF0",temp_str2,temp_str1,"颜色分量信息:每个分量有三个字节,第一个为分量的ID,01:Y 02:U 03:V;第二个字节高位为水平采样因子,低位为垂直采样因子。"); sprintf(temp_str2,"对应量化表ID【%d】",i); itoa((int)Q_table,temp_str1,10); priv->dlg->AppendBInfo("SOF0",temp_str2,temp_str1,"颜色分量信息:第三个字节代表这个分量对应的量化表ID,例如,Y对应的量化表ID索引值为00,而UV对应的量化表ID都为01,即它们共用一张量化表。"); //------------- #if TRACE_PARAM fprintf(param_trace,"Component:%d factor:%dx%d Quantization table:%d\n", cid, c->Hfactor, c->Hfactor, Q_table ); fflush(param_trace); #endif } priv->width = width; priv->height = height; #if TRACE_PARAM fprintf(param_trace,"< SOF marker\n"); fflush(param_trace); #endif return 0; }
parse_DHT()用于解析DHT标签:
//解析DHT表 static int parse_DHT(struct jdec_private *priv, const unsigned char *stream) { unsigned int count, i,j; unsigned char huff_bits[17]; int length, index; //------------------------------------------ char *temp; FILE *fp; //------------------------------------------ length = be16_to_cpu(stream) - 2; //跳过length字段 stream += 2; /* Skip length */ #if TRACE_PARAM fprintf(param_trace,"> DHT marker (length=%d)\n", length); fflush(param_trace); #endif while (length>0) { //跳过第1字节: //Huffman 表ID号和类型,高 4 位为表的类型,0:DC 直流;1:AC交流 //低四位为 Huffman 表 ID。 index = *stream++; /* We need to calculate the number of bytes 'vals' will takes */ huff_bits[0] = 0; count = 0; //不同长度 Huffman 的码字数量:固定为 16 个字节,每个字节代表从长度为 1到长度为 16 的码字的个数 for (i=1; i<17; i++) { huff_bits[i] = *stream++; //count记录码字的个数 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_PARAM fprintf(param_trace,"Huffman table %s[%d] length=%d\n", (index&0xf0)?"AC":"DC", index&0xf, count); fflush(param_trace); #endif #endif if (index & 0xf0 ){ //--------------------- char temp_str1[MAX_URL_LENGTH]={0}; char temp_str2[MAX_URL_LENGTH]={0}; temp=(char *)stream; //fp = fopen("DHT.txt", "a+"); //fwrite(temp, 16, 1, fp); for(j=0;j<16;j++){ //fprintf(fp,"%d ",temp[j]); sprintf(temp_str2,"%d ",temp[j]); strcat(temp_str1,temp_str2); } //fprintf(fp,"\n-----------------------\n"); //fclose(fp); //----------------------------------------------------- priv->dlg->AppendBInfo("DHT","定义霍夫曼表【交流系数表】",temp_str1,"Huffman表ID号和类型:1字节,高4位为表的类型,0:DC直流;1:AC交流 可以看出这里是直流表;低四位为Huffman表ID"); //----------------------------------------------------- //交流霍夫曼表 build_huffman_table(huff_bits, stream, &priv->HTAC[index&0xf]); } else{ //--------------------- char temp_str1[MAX_URL_LENGTH]={0}; char temp_str2[MAX_URL_LENGTH]={0}; temp=(char *)stream; //fp = fopen("DHT.txt", "a+"); //fwrite(temp, 16, 1, fp); for(j=0;j<16;j++){ //fprintf(fp,"%d ",temp[j]); sprintf(temp_str2,"%d ",temp[j]); strcat(temp_str1,temp_str2); } //fprintf(fp,"\n-----------------------\n"); //fclose(fp); //----------------------------------------------------- priv->dlg->AppendBInfo("DHT","定义霍夫曼表【直流系数表】",temp_str1,"Huffman表ID号和类型:1字节,高4位为表的类型,0:DC直流;1:AC交流 可以看出这里是直流表;低四位为Huffman表ID"); //----------------------------------------------------- //直流霍夫曼表 build_huffman_table(huff_bits, stream, &priv->HTDC[index&0xf]); } length -= 1; length -= 16; length -= count; stream += count; } #if TRACE_PARAM fprintf(param_trace,"< DHT marker\n"); fflush(param_trace); #endif return 0; }
parse_DQT()用于解析DQT标签:
//解析Define quantization table static int parse_DQT(struct jdec_private *priv, const unsigned char *stream) { int qi; float *table; const unsigned char *dqt_block_end; //------------------------------------------------ int j,k; char *temp; FILE *fp; //------------------------------------------------ #if TRACE_PARAM fprintf(param_trace,"> DQT marker\n"); fflush(param_trace); #endif //该Segment末尾 dqt_block_end = stream + be16_to_cpu(stream); //跳过标签length(2字节) stream += 2; /* Skip length */ //没到末尾 while (stream < dqt_block_end) { //跳过该Segment的第1个字节,QT信息 //precision: 00 (Higher 4 bit) //index: 00 (Lower 4 bit) //qi取1,第1张量化表(例如,亮度表),取2,第2张量化表(例如,色度表) 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指向jdec_private的Q_tables数组,为了在其中写入数据 table = priv->Q_tables[qi]; //注意:一次搞定整张!写入 //需要对数值进行变换!cos(k*PI/16) * sqrt(2) //这样才能得到离散余弦变换的系数 build_quantization_table(table, stream); //---------------------------------------------------------- temp=(char *)stream; //fp = fopen("DQT.txt", "a+"); //fwrite(temp, 64, 1, fp); char temp_str1[MAX_URL_LENGTH]={0}; char temp_str2[MAX_URL_LENGTH]={0}; for(j=0;j<64;j++){ sprintf(temp_str2,"%d ",temp[j]); strcat(temp_str1,temp_str2); //fprintf(fp,"%d ",temp[j]); } //计数 char temp_str3[MAX_URL_LENGTH]={0}; sprintf(temp_str3,"量化表【%d】",priv->DQT_table_num); priv->dlg->AppendBInfo("DQT",temp_str3,temp_str1,"JPEG格式文件的量化表,一般来说第一张是亮度的,后面是色度的"); priv->DQT_table_num++; //fprintf(fp,"\n-----------------------\n"); //fclose(fp); #if TRACE_PARAM for(j=0;j<8;j++){ for(k=0;k<8;k++){ fprintf(param_trace,"%d ",temp[j*8+k]); } fprintf(param_trace,"\n"); } fprintf(fp,"\n-----------------------\n"); fflush(param_trace); #endif //---------------------------------------------------------- //完事了! stream += 64; } #if TRACE_PARAM fprintf(param_trace,"< DQT marker\n"); fflush(param_trace); #endif return 0; }
其他标签的解析不一一列举。
待续未完。。。
主页:http://www.saillard.org/programs_and_patches/tinyjpegdecoder/
源代码下载:http://download.csdn.net/detail/leixiaohua1020/6383115
相关推荐
tiny jpeg decoder 是可以用于嵌入式系统的jpeg解码器,也可以在windows下编译通过。
压缩包中的"tinyjpegdecoder-20070609.tar.bz2"文件是Tiny JPEG Decoder的源代码包,日期为2007年6月9日,可能包含了编译说明、示例代码和其他相关资源。"welcome.txt"可能是对项目的一个简短介绍或使用指南。 学习...
作为从jpge小组文档中移植的代码,tinyjpeg可能是开源的,允许开发者查看源代码、学习解码技术,并对其进行改进。这有助于整个图像处理社区的技术交流和发展。 总的来说,tinyjpeg是一个实用的JPEG解码工具,它的...
Arduino TJpg_Decoder库该Arduino库支持将存储在SD卡上以及程序存储器(FLASH)中的阵列中的Jpeg文件呈现到TFT显示器上。 此外,存储在SPIFFS Flash归档系统或“ PROGMEM”阵列中的图像可以与ESP8266和ESP32处理器...
1. **程序结构**:将JPEG解码、SD卡读取和TTF显示功能模块化,便于代码管理和维护。 2. **调试**:使用如STM32CubeIDE、Keil uVision等开发工具进行调试,确保每个模块的功能正常,优化性能。 3. **测试**:在实际...
1. `tjpgd.c`:这是主要的JPEG解码器实现文件,包含了解码算法的核心逻辑。它将JPEG编码的二进制数据转换为RGB或灰度图像的原始像素数据,以便在STM32的显示屏上显示或者进行其他处理。 2. `jpegdecode.c`:可能包含...
Tiny_Jpeg 微型 Jpeg 解码器 Luc Saillard GNU 公共许可证的原始代码 2007 年 6 月 9 日 tinyjpegdecoder-20070609.... 2个流行的公共Jpeg解码器小代码之一 Nano Jpeg Martin Fiedler东德 Tiny Jpeg Luc Saillard 法国
**源代码分析** 源代码是程序员用高级语言书写的程序文本,它是编译器工作的输入。在这个压缩包中,你将找到`Tiny+`编译器的源代码,这将使你有机会深入到编译器的内部,观察其如何处理源代码的每一个细节。 **...
此外,`请阅读此文件.txt` 可能包含了项目介绍、使用说明或者注意事项,而 `a.txt` 和 `b.txt` 可能是测试用例的 Tiny+ 源代码文件,用于验证编译器的功能是否正确。 学习 Tiny+ 编译器源代码,不仅可以加深对...
Tiny JPEG Decompressor的源代码简洁明了,易于理解和移植。开发者可以轻松地根据自己的需求进行定制和优化。源码的组织结构清晰,主要分为解码核心部分和平台相关的I/O函数。解码核心部分负责处理JPEG的编码格式,...
《TINY编译器源代码》是针对C语言实现的一款小型编译器,它能够将输入的源代码文件转换为对应的机器码,从而在计算机上执行。这一项目通常与《编译原理与实践》这本教材相结合,为学习编译器设计与实现的初学者提供...
【TINYC tiny c最快的c编译器源代码.rar】是一个包含TINYC C编译器源代码的压缩包。TINYC是一个小型且快速的C语言编译器,它被设计为轻量级,适合在资源有限的环境中使用,如嵌入式系统或老旧的计算机上。该压缩包中...
2. **源代码分析** 在`tinyxml_2_5_3.rar`中,包含了TinyXML的源代码,通过阅读源代码,你可以了解到XML解析器的工作原理。例如,`TiXmlElement`类如何存储元素名和属性,`TiXmlNode`如何构建节点树,以及`...
1. **词法分析**(Lexical Analysis):这是编译器的第一个阶段,负责将源代码转换成一个个称为“标记”(Token)的最小单元。这些标记通常包括关键字、标识符、常量、运算符等。在这个阶段,源代码的文本被分割成有...
- **备注**:在Linux-2.6.36/38内核版本中,多媒体驱动程序通常以目标文件形式存在,而非源代码形式。 ##### 25. NAND Flash 驱动 - **驱动程序源代码位置**:`Linux-2.6.38/drivers/mtd/nand` - **设备名**:无...
语法分析器是编译器设计的关键组成部分,它负责将源代码转换为抽象语法树(AST),这是理解程序结构的关键步骤。本项目以C语言为工具,实现了一个语法分析器,采用了两种不同的方法:递归下降分析法和LL(1)语法分析...
•完成并提交实验报告,扫描程序的源程序,编译后的可执行程序,例子和运行结果. 实验报告至少要包含如下内容: 1 实验目的; 2 TINYC语言的词法说明,扫描器的输入和输出; 3 实验原理(所采用的过程); 3.1 记号种类及各...
这个是友善之臂专题8的源代码,不过没有注释,现在加上了注释,提供下载链接,也可以不下载,对照着这里看。因为这个过程包括采集、硬编码、软解码等过程。 调用的函数是规范的,所以不是用tiny6410的也可以看看,...