JPEG解码实验
来源:互联网 发布:java iterator() 编辑:程序博客网 时间:2024/06/16 06:43
一、JPEG编解码原理
JPEG编码的过程如上图所示。解码是编码的逆过程。
以下是编码具体说明:
1、零偏置(Level Offset)
对于灰度级是2的n次方的像素,通过减去2的n-1次方,将无符号的整数值变成有符号数。例如:对于n=8,即将0~255的值域,通过减去128,转换为值域在-128~127之间的值。这样做的目的是:使像素的绝对值出现3位10进制的概率大大减少。
2、8*8DCT
将输入图像分为8*8的小块, 每个块里有64个像素。若边缘未满8*8,则用边缘像素进行填充。
总体来说,图像的低频部分集中在每个8*8块的左上角,高频部分在右下角。DCT变化可以做到以下三点:1、能量守恒 2、能量集中 3、去相关。
3、量化
根据人眼的视觉特性(对低频敏感,对高频不太敏感)对低频分量采取较细的量化,对高频分量采取较粗的量化。 由于上面的人眼视觉特性,量化表左上角的值较小,右上角的值较大,这样就起到了保持低频分量,抑制高频分量的目的。
4、DC系数的差分编码
8X8图像块经过DC丁变换之后得到的DC直流系数有两个特点:1.系数的数值比较大2.相邻8X8图像块的DC系数值变化不大。根据这个特点,JPEG算法使用了差分脉冲调制编码(DPCM)技术,对相邻图像块之间量化DC系数的差值DIFF进行编码:DIFFk =DCk - DCk-1。
5、AC系数的Z字扫描
由于经DCT变换后,系数大多数集中在左上角,即低频分量区,因此采用Z字形按频率的高低顺序读出,可以出现很多连零的机会。可以使用游程编码。尤其在最后,如果都是零,给出EOB (End of Block)即可。
6、AC系数的游程编码
在JPEG和MPEG编码中规定为:(run, level),表示连续run个0,后面跟值为level的系数。
如:0,2,0,0,3,0,-4,0,0,0,-6,0,0,5,7表示为(1, 2), (2, 3) ,…
编码:
Run: 最多15个,用4位表示RRRR。
Level:类似DC:分成16个类别,用4位表示SSSS表示类别号;类内索引。对(RRRR, SSSS)联合用Huffman编码;对类内索引用定长码编码
AC系数的游程编码
在JPEG和MPEG编码中规定为:(run, level),表示连续run个0,后面跟值为level的系数。
如:0,2,0,0,3,0,-4,0,0,0,-6,0,0,5,7表示为(1, 2), (2, 3) ,…
编码:
Run: 最多15个,用4位表示RRRR。
Level:类似DC:分成16个类别,用4位表示SSSS表示类别号;类内索引。对(RRRR, SSSS)联合用Huffman编码;对类内索引用定长码编码
二、JPEG文件格式
- SOI,Start of Image,图像开始
- APP0,Application,应用程序保留标记0
- DQT,Define Quantization Table,定义量化表
- SOF0,Start of Frame,帧图像开始
- DHT,Define Huffman Table,定义哈夫曼表
- SOS,Start of Scan,扫描开始12字节
- EOI,End of Image,图像结束2字节
标记代码 2字节 固定值0xFFD8
标记代码 2字节 固定值0xFFD9
APP0应用程序保留标记0
① 数据长度 2字节 ①~⑨9个字段的总长度
②标识符 5字节 固定值0x4A46494600,即字符串“JFIF0”
③ 版本号 2字节 一般是0x0102,表示JFIF的版本号1.2
④ X和Y的密度单位 1字节 只有三个值可选
0:无单位;1:点数/英寸;2:点数/厘米
⑤ X方向像素密度 2字节 取值范围未知
⑥ Y方向像素密度 2字节 取值范围未知
⑦ 缩略图水平像素数目 1字节 取值范围未知
⑧ 缩略图垂直像素数目 1字节 取值范围未知
⑨ 缩略图RGB位图 长度可能是3的倍数 缩略图RGB位图数据
DQT 定义量化表
① 数据长度 2字节 字段①和多个字段②的总长度
② 量化表 数据长度-2字节
a)精度及量化表ID 1字节
高4位:精度,只有两个可选值 0:8位;1:16位
低4位:量化表ID,取值范围为0~3
b)表项 (64×(精度+1))字节
例如8位精度的量化表,其表项长度为64×(0+1)=64字节
① 数据长度 2字节 ①~⑥六个字段的总长度
②精度 1字节 每个数据样本的位数
通常是8位,一般软件都不支持 12位和16位
③ 图像高度 2字节 图像高度(单位:像素)
1:灰度图;3:YCrCb或YIQ;4:CMYK
而JFIF中使用YCrCb,故这里颜色分量数恒为3
⑥颜色分量信息 颜色分量数×3字节(通常为9字节)
b)水平/垂直采样因子 1字节
高4位:水平采样因子低4位:垂直采样因子
c)量化表 1字节 当前分量使用的量化表的ID
① 数据长度 2字节
② huffman表 数据长度-2字节
高4位:类型,只有两个值可选
0:DC直流;1:AC交流
低4位:哈夫曼表ID,
注意,DC表和AC表分开编码
本标记段中,字段②可以重复出现(一般4次),也可以只出现1次。
①数据长度 2字节 ①~④两个字段的总长度
②颜色分量数 1字节 应该和SOF中的字段⑤的值相同,即:
1:灰度图是;3: YCrCb或YIQ;4:CMYK。
a)颜色分量ID 1字节
b) 直流/交流系数表号 1字节
高4位:直流分量使用的哈夫曼树编号
低4位:交流分量使用的哈夫曼树编号
a)谱选择开始 1字节 固定值0x00
b)谱选择结束 1字节 固定值0x3F
c)谱选择 1字节 在基本JPEG中总为00
struct huffman_table,struct component ,struct jdec_private三个结构体设计的目的如下:struct jdec_private处于最上层,支配整个图像,其中包含有 struct huffman_table和struct component。 struct huffman_table用来存放DC系数和AC系数的Huffman表;struct component用来存放8x8数据块组成的MCU即最小数据单元的数值,相当于移动像块,每次访问下一像块就给该结构体更新数值。
{ /* 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 { unsigned int Hfactor;//水平采样因子 unsigned int Vfactor;//垂直采样因子 float *Q_table; /* Pointer to the quantisation table to use */ struct huffman_table *AC_table;//<span style="color:black;"><span style="color:black;"><span style="color:black;">DC系数的Huffman表</span></span></span> struct huffman_table *DC_table;//<span style="color:black;"></span></span><span style="font-size:12px;">AC<span style="color:black;"></span></span><span style="font-size:12px;">系数的Huffman表 short int previous_DC; /* Previous DC coefficient */ short int DCT[64]; /* DCT coef */ #if SANITY_CHECK unsigned int cid; #endif }; typedef void (*decode_MCU_fct) (struct jdec_private *priv); typedef void (*convert_colorspace_fct) (struct jdec_private *priv); 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]; /*by liushasha*///添加两个指针用来存放将要输出的DC、AC的图像。 short int*DC_Image; short int*AC_Image; /*by liushasha*/ jmp_buf jump_state; /* Internal Pointer use for colorspace conversion, do not modify it !!! */ uint8_t *plane[COMPONENTS]; }
enum tinyjpeg_fmt { TINYJPEG_FMT_GREY = 1, TINYJPEG_FMT_BGR24, TINYJPEG_FMT_RGB24, TINYJPEG_FMT_YUV420P, /*by liushasha*///将YUV分量合成YUV文件输出 TINYJPEG_FMT_YUV, /*by liushasha*/ }
{ ...... switch (pixfmt) { /*by liushasha*///对TINYJPEG_FMT_YUV的处理和对TINYJPEG_FMT_YUV420P的处理完全一致 case TINYJPEG_FMT_YUV: ...... break; case TINYJPEG_FMT_YUV420P: <span style="color:black;"></span></span><pre name="code" class="cpp"><span style="font-size:12px;"> ...... break; case TINYJPEG_FMT_RGB24: <span style="color:black;"></span></span><pre name="code" class="cpp"><pre name="code" class="cpp"><span style="font-size:12px;"> ...... break; case TINYJPEG_FMT_BGR24: ...... </span></pre> <pre></pre> <span style="font-size:12px"><span style="color:black"></span></span><pre name="code" class="cpp"><pre name="code" class="cpp"><pre name="code" class="cpp"><pre name="code" class="cpp"><span style="font-size:12px;"> break;</span></pre> <pre></pre> <pre></pre> <pre></pre> <span style="font-size:12px"> case TINYJPEG_FMT_GREY:<span style="color:black"></span></span><pre name="code" class="cpp"><pre name="code" class="cpp"><pre name="code" class="cpp"><pre name="code" class="cpp"><span style="font-size:12px;"> ...... </span></pre> <pre></pre> <pre name="code" class="cpp"><pre name="code" class="cpp"><pre name="code" class="cpp"><pre name="code" class="cpp"><span style="font-size:12px;"> break;</span></pre> <pre></pre> <pre></pre> <pre></pre> <pre></pre> <pre></pre> <span style="font-size:12px">default;......}...... return 0;</span> <pre></pre> <span style="font-size:12px">}<br> </span> <pre></pre> <pre name="code" class="cpp"><span style="font-size:12px;">int convert_one_image(const char *infilename, const char *outfilename, int output_format) { ...... 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; /*by liushasha*/ <span style="color:black;"></span></span><pre name="code" class="cpp"><span style="font-size:12px;"> case TINYJPEG_FMT_YUV:</span></pre><span style="font-size:12px"><span style="color:black"></span></span><pre name="code" class="cpp"><span style="font-size:12px;"> write_add_yuv(outfilename, width, height, components); break; /*by liushasha*/ </span></pre><span style="font-size:12px"> case TINYJPEG_FMT_GREY:<br> write_pgm(outfilename, width, height, components);<br> break;<br> }<br> ......<br> }</span><br> <pre></pre> <pre name="code" class="cpp"><span style="font-size:12px;">/*by liushasha*///YUV文件写出 static void write_add_yuv(const char *filename, int width, int height, unsigned char **components) { FILE *F; char temp[1024]; 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); } /*by liushasha*/<span style="color:black;"></span> <span style="color:black;"></span></span></pre><pre name="code" class="cpp"><span style="font-size:12px;">static void usage(void) { fprintf(stderr, "Usage: loadjpeg [options] <input_filename.jpeg> <format> <output_filename>\n"); fprintf(stderr, "options:\n"); fprintf(stderr, " --benchmark - Convert 1000 times the same image\n"); fprintf(stderr, "format:\n"); fprintf(stderr, " yuv420p - output 3 files .Y,.U,.V\n"); /*by liushasha*/ fprintf(stderr, " yuv - output a files.YUV\n"); /*by liushasha*/ fprintf(stderr, " rgb24 - output a .tga image\n"); fprintf(stderr, " bgr24 - output a .tga image\n"); fprintf(stderr, " gray - output a .pgm image\n"); exit(1); }</span></pre><span style="font-size:12px"></span><pre name="code" class="cpp">int main(int argc, char *argv[]) { ...... /*by liushasha*/ #if TABLES char temp[1024]; snprintf(temp,1024,"%s_tables.txt",ouput_filename); #endif /*by liushasha*/ ...... if (strcmp(argv[current_argument+1],"yuv420p")==0) output_format = TINYJPEG_FMT_YUV420P; /*by liushasha*/ else if (strcmp(argv[current_argument+1],"yuv")==0) output_format = TINYJPEG_FMT_YUV; /*by liushasha*/ 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 ...... /*by liushasha*/ #if TABLES fclose(f_tables); #endif /*by liushasha*/ return 0; } </pre>在tinyjepg.h中定义<br> <pre name="code" class="cpp">/*by liushasha*/ #define TABLES1 FILE *f_tables; /*by liushasha*/</pre><pre name="code" class="cpp">static int parse_DQT(struct jdec_private *priv, const unsigned char *stream) { ...... while (stream < dqt_block_end)//检查是否还有表 {...... /*by liushasha*/ #if TABLES fprintf(f_tables,"Quantization_table[%d]:\n",qi); fflush(f_tables); #endif /*by liushasha*/ ...... } ...... } </pre><pre name="code" class="cpp">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++) { /*by liushasha*/ #if TABLES fprintf(f_tables,"%d\t",ref_table[*zz]); fflush(f_tables); if(j==7) { fprintf(f_tables,"%\n"); fflush(f_tables); } #endif <span style="color:black;"></span><pre name="code" class="cpp"> /*by liushasha*/</pre> *qtable++ = ref_table[*zz++] * aanscalefactor[i] * aanscalefactor[j]; } }} <pre></pre> <pre name="code" class="cpp">static int parse_DHT(struct jdec_private *priv, const unsigned char *stream) { ...... if (index & 0xf0 ) { /*by liushasha*/ #if TABLES fprintf(f_tables,"Huffman table AC[%d]length=%d\n",index & 0xf,count); fflush(f_tables); #endif /*by liushasha*/ build_huffman_table(huff_bits, stream, &priv->HTAC[index&0xf]); } else { /*by liushasha*/ #if TABLES fprintf(f_tables,"Huffman table DC[%d]length=%d\n",index & 0xf,count); fflush(f_tables); #endif /*by liushasha*/ build_huffman_table(huff_bits, stream, &priv->HTDC[index&0xf]); } ...... } ...... } </pre><pre name="code" class="cpp">static void build_huffman_table(const unsigned char *bits, const unsigned char *vals, struct huffman_table *table) { ...... for (i=0; huffsize[i]; i++) { ...... /*by liushasha*/ #if TABLES fprintf(f_tables,"val==%2.2x code=%8.8x codesize=%2.2d\n",val, code, code_size); fflush(p_tables); #endif /*by liushasha*/ ...... } } </pre>tinyjpeg.h<br> <pre name="code" class="cpp">/*liushasha*/ FILE *DC_FILE; FILE *AC_FILE; static void output_DC_Image(struct jdec_private *priv); static void output_AC_Image(struct jdec_private *priv); static void output_DC_AC_Image(struct jdec_private *priv); /*liushasha*/</pre>tinyjepg-internal.h<br> <pre name="code" class="cpp">struct jdec_private { ...... /*by liushasha*/ short int *DC_Imaage; short int *AC_Imaage; /*by liushasha*/ ...... }; </pre><pre name="code" class="cpp">int main(int argc, char *argv[]) { ...... /*by liushasha*/ char temp[1024]; snprintf(temp,1024,"%s_dc.y",output_filename); DC_FILE=fopen(temp,"wb"); snprintf(temp,1024,"%s_ac.y",output_filename); AC_FILE=fopen(temp,"wb"); /*by liushasha*/ ...... /*by liushasha*/ fclose(AC_FILE); fclose(DC_FILE); /*by liushasha*/ return 0; } </pre><pre name="code" class="cpp">int tinyjpeg_decode(struct jdec_private *priv, int pixfmt) { ...... /*by liushasha*/ priv->DC_Image=(short int *)malloc(sizeof(short int)*priv->height*priv->width/64); priv->AC_Image=(short int *)malloc(sizeof(short int)*priv->height*priv->width/64); /*by liushasha*/ ...... return 0; } </pre>以<span style="color:black"></span>decode_MCU_1x1plane为例添加代码,其他的类似。<br> <pre name="code" class="cpp">static void decode_MCU_1x1_1plane(struct jdec_private *priv) { // Y process_Huffman_data_unit(priv, cY); /*by liushasha*/ output_DC_Image(priv); output_AC_Image(priv); /*by liushasha*/ IDCT(&priv->component_infos[cY], priv->Y, 8); // Cb process_Huffman_data_unit(priv, cCb); IDCT(&priv->component_infos[cCb], priv->Cb, 8); // Cr process_Huffman_data_unit(priv, cCr); IDCT(&priv->component_infos[cCr], priv->Cr, 8); } </pre><pre name="code" class="cpp">/*by liushasha*/ static void output_DC_Image(struct jdec_private *priv) { static long int i=0; if(i<priv->height*priv->width/64) priv->DC_Image[i]=priv->component_infos[0].DCT[0]; i++; } static void output_AC_Image(struct jdec_private *priv) { static long int i=0; if(i<priv->height*priv->width/64) priv->AC_Image[i]=priv->component_infos[0].DCT[1]; i++; } /*by liushasha*/ </pre><pre name="code" class="cpp">void tinyjpeg_free(struct jdec_private *priv) { int i; for (i=0; i<COMPONENTS; i++) { if (priv->components[i]) free(priv->components[i]); priv->components[i] = NULL; } /*by liushasha*/ if(priv->AC_Image) free(priv->AC_Image); if(priv->DC_Image) free(priv->DC_Image); /*by liushasha*/ free(priv); }</pre><pre name="code" class="cpp">/*by liushasha*/ static void write_DC_AC_Image(struct jdec_private *priv) { int i; short int DC_min, DC_max, AC_min, AC_max; unsigned char *temp; DC_min = priv->DC_Image[0]; DC_max = priv->DC_Image[0]; AC_min = priv->AC_Image[0]; AC_max = priv->AC_Image[0]; temp = (unsigned char*)malloc(priv->height*priv->width / 64); for (i = 0; i < (priv->height*priv->width/64); i++) { if (priv->DC_Image[i] > DC_max) DC_max = priv->DC_Image[i]; if (priv->DC_Image[i] < DC_min) DC_min = priv->DC_Image[i]; if (priv->AC_Image[i] > AC_max) AC_max = priv->AC_Image[i]; if (priv->AC_Image[i] < AC_min) AC_min = priv->AC_Image[i]; } for (i = 0; i < (priv->height*priv->width/64); i++) { temp[i] = (unsigned char)255 * (priv->DC_Image[i] - DC_min) / (DC_max - DC_min); } fwrite(temp, 1, priv->width*priv->height / 64, DC_FILE); for (i = 0; i < (priv->height*priv->width / 64); i++) { temp[i] = (unsigned char)255 * (priv->AC_Image[i] - AC_min) / (AC_max - AC_min); } fwrite(temp, 1, priv->width*priv->height / 64, AC_FILE); if (temp) free(temp); }
可观看的 yuv文件:
输出DC图像并经过huffman统计其概率分布:
输出某一个AC值图像并统计其概率分布:
- JPEG解码实验
- 【实验五】JPEG解码
- 实验五:JPEG解码
- 实验五 JPEG解码
- 数据压缩实验五:JPEG解码
- 数据压缩实验五:JPEG解码
- 数据压缩实验无--jpeg解码
- 实验五——JPEG编解码
- 数据压缩实验五:JPEG文件解码实验分析
- JPEG解码
- jpeg解码
- JPEG解码
- JPEG解码
- jpeg解码
- 《数据压缩》实验报告五·JPEG编解码
- S3C6410的JPEG解码
- Symbian (JPEG)解码总结
- JPEG编解码详解
- 关于restful协议很多人的误解
- Codeforces Round #417 (Div. 2) A. Sagheer and Crossroads
- mysql慢查询日志
- Wait/Notify
- 数据库导入与导出
- JPEG解码实验
- 玲珑杯1128--线段树模板
- CentOS7 下 python2.7.5 安装 pip 后再安装 requests包
- Virtualbox如何配置Linux的网络连接
- 灯泡开关案例
- Linux下socket编程之多进程TCP服务器端
- android Snackbar
- 非参数估计:parzen窗发与kn近领法
- PlaidCTF2017之zipper