数据压缩原理实验5_JPEG编解码原理及代码分析
来源:互联网 发布:泛微协同办公软件 编辑:程序博客网 时间:2024/06/06 18:28
一、实验原理
1、JPEG编解码原理
JPEG 是Joint Photographic Experts Group(联合图像专家小组)的缩写,是第一个国际图像压缩标准。JPEG图像压缩算法能够在提供良好的压缩性能的同时,具有比较好的重建质量,被广泛应用于图像、视频处理领域。”.jpeg”、”.jpg”等指代的是图像数据经压缩编码后在媒体上的封存形式。
下图为编码的流程图:
解码为编码的逆过程,流程图如下:
编码的具体过程如下:
(1)8*8DCT
8*8的块分割
将输入图像分为8*8的小块, 每个块里有64个像素。若边缘未满8*8,则用边缘像素进行填充(不建议用黑或白像素填充的原因是可能会破坏图像的原有结构)。
零偏置
对于灰度级是
对于n=8,即将0~255的值域,通过减去128,转换为值域在-128~127之间的值
目的:使像素的绝对值出现3位10进制的概率大大减少
DCT变换
下图为DCT变换的公式:
下图为DCT谱中其中6个频谱特性图:
由上图可见,第一行是图像行方向上分别从等值,半周余弦,一周余弦、一周半余弦…第一列是图像列方向上也是该规律,而其他的DCT值则是两者交织共同组成的,表现了整个图像不同频率上的细节成分。总体来说,图像的低频部分集中在每个8*8块的左上角,高频部分在右下角。
(2)量化
因为人眼对亮度信号比对色差信号更敏感,因此使用了两种量化表:亮度量化值和色差量化值。
根据人眼的视觉特性(对低频敏感,对高频不太敏感)对低频分量采取较细的量化,对高频分量采取较粗的量化。
如果原始图象中细节丰富,则去掉的数据较多,量化后的系数与量化前差别;反之,细节少的原始图象在压缩时去掉的数据少些。
而量化就是用像素值÷量化表对应值所得的结果。由于上面的人眼视觉特性,量化表左上角的值较小,右上角的值较大,这样就起到了保持低频分量,抑制高频分量的目的。
下图为人眼视觉的敏感度:
(3)编码
DC系数差分编码
8X8图像块经过DC丁变换之后得到的DC直流系数有两个特点
- 系数的数值比较大
- 相邻8X8图像块的DC系数值变化不大:冗余
根据这个特点,JPEG算法使用了差分脉冲调制编码(DPCM)技术,对相邻图像块之间量化DC系数的差值DIFF进行编码:
AC系数的Z字扫描、游程编码
Z字扫描
由于经DC丁变换后,系数大多数集中在左上角,即低频分量区,因此采用Z字形按频率的高低顺序读出,可以出现很多连零的机会。可以使用游程编码。尤其在最后,如果都是零,给出EOB(End of Block)即可。
下图为Z字扫描的扫描顺序:游程编码
在JPEG和MPEG编码中规定为:(run, level)
表示连续run个0,后面跟值为level的系数
Run : 最多15个,用4位表示RRRR
Level : 类似DC,分成16个类别,用4位表示SSSS表示类别号、类内索引
对(RRRR, SSSS)联合用Huffman编码
对类内索引用定长码编码
2、JPEG文件格式
(1)量化表DQT
一般为两个量化表,即亮度和色度各一张,以0xFFDB开始
量化表长度,一般为00 43(或00 84)
量化表信息(1字节)
- Bit 0~3 QT号(只能取值为0~3,否则错误)
- Bit 4~7 QT精度(0为8比特,否则表示16比特)
量化表的实际数据
- 量化表中的数据按照Z字形保存量化表内8*8的数据
(2)DHT霍夫曼表
Huffman表长度:2字节
Huffman表ID号和类型:共1字节,高4位为表的类型,其中0表示DC直流,1表示AC交流;低4位为Huffman表ID
Huffman表位表:16字节的编码,其代码代数和为接下来的编码长度。
Huffman表值表:内容编码信息表示每个霍夫曼码字对应的值
二、代码分析
1、将输出文件保存为YUV文件
tinyjpeg.h
enum tinyjpeg_fmt { ... /* add by zx */ TINYJPEG_FMT_YUV420, /* end */};
tinyjpeg.c
int tinyjpeg_decode(struct jdec_private *priv, int pixfmt){ /* add by zx */ case TINYJPEG_FMT_YUV420: colorspace_array_conv = convert_colorspace_yuv420p; if (priv->components[0] == NULL) priv->components[0] = (uint8_t *)malloc(priv->width * priv->height); if (priv->components[1] == NULL) priv->components[1] = (uint8_t *)malloc(priv->width * priv->height/4); if (priv->components[2] == NULL) priv->components[2] = (uint8_t *)malloc(priv->width * priv->height/4); bytes_per_blocklines[0] = priv->width; bytes_per_blocklines[1] = priv->width/4; bytes_per_blocklines[2] = priv->width/4; bytes_per_mcu[0] = 8; bytes_per_mcu[1] = 4; bytes_per_mcu[2] = 4; break; /* end */}
loadjpeg.c
/* add by zx *//*** Save a buffer in a file (.YUV) useable by yuvsplittoppm*/static void write_yuv_all(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);}/* end */
int load_multiple_times(const char *filename, const char *outfilename, int output_format){ ... switch (output_format) { ... /* add by zx */ case TINYJPEG_FMT_YUV420: write_yuv_all(outfilename, width, height, components); break; /* end */ ... } ...}
int main(int argc, char *argv[]){ ... if (strcmp(argv[current_argument+1],"yuv420p")==0) output_format = TINYJPEG_FMT_YUV420P; /* add by zx */ else if (strcmp(argv[current_argument + 1], "yuv420") == 0) output_format = TINYJPEG_FMT_YUV420; /* end */ ... else ...}
2、以txt文件输出所有的量化矩阵和所有的HUFFMAN码表
tinyjpeg.h
/* add by zx */ #define TABLES 1FILE *p_tables;/* end */
loadjpeg.c
int main(int argc, char *argv[]){.../* add by zx */ #if TABLES snprintf(temp, 1024, "tables_of_%s.txt", output_filename); p_tables = fopen(temp, "w");#endif/* end */.../* add by zx */ #if TABLES fclose(p_tables);#endif/* end */...}
tinyjpeg.c
static void build_huffman_table(const unsigned char *bits, const unsigned char *vals, struct huffman_table *table){ ... /* add by zx */#if TABLES /*分别输出:huffman码长、对应码字、对应符号。*/ fprintf(p_tables, "val=%2.2x code=%8.8x codesize=%2.2d\n", val, code, code_size); fflush(p_tables);#endif /* end */ ...}
static void build_quantization_table(float *qtable, const unsigned char *ref_table){ ... for (i=0; i<8; i++) { for (j=0; j<8; j++) { *qtable++ = ref_table[*zz++] * aanscalefactor[i] * aanscalefactor[j]; /* add by zx */ #if TABLES // ref_table指针中存取着之字形扫描的量化表值,因此需要利用zigzag[64]进行正确的排序 fprintf(p_tables, "%2d\t", ref_table[*(zigzag+i * 8 + j)]); //用于限定一行输出8个量化值,使量化表以8×8的二维矩阵的形式输出 if (j == 7) fprintf(p_tables, "\n"); fflush(p_tables);#endif /* end */ } } ...}
static int parse_DQT(struct jdec_private *priv, const unsigned char *stream){ ... while (stream < dqt_block_end) { qi = *stream++; /* add by zx */ #if TABLES fprintf(p_tables, "Quantization_tables [%d] \n", qi); fflush(p_tables);#endif /* end */ table = priv->Q_tables[qi]; build_quantization_table(table, stream); stream += 64; } ...}
static int parse_DHT(struct jdec_private *priv, const unsigned char *stream){... while (length>0) { ... /* add by zx */ #if TABLES //输出AC交流及DC直流Huffman表表头 其ID以及长度 fprintf(p_tables,"Huffman table %s[%d] length=%d\n", (index&0xf0)?"AC":"DC", index&0xf, count); fflush(p_tables); #endif /* end */ ... }...}
3、输出DC图像及某个AC图像
tinyjpeg-internal.h
struct jdec_private{ /* add by zx */ // 用于存放输出的DC、AC图像 short int *DC_Image; short int *AC_Image; /* end */}
tinyjpeg.h
/* add by zx */FILE *DC_FILE;FILE *AC_FILE;static void output_DC_AC(struct jdec_private *priv); //用于输出图像static void mapping_DC_AC(struct jdec_private *priv); //用于限幅映射/* end */
loadjpeg.c
int main(int argc, char *argv[]){ ... /* add by zx */ char temp[1024]; snprintf(temp, 1024, "DC_%s.yuv", output_filename); DC_FILE = fopen(temp, "wb"); if (DC_FILE == NULL) { printf("DC_FILE file open failed"); } snprintf(temp, 1024, "AC_%s.yuv", output_filename); AC_FILE = fopen(temp, "wb"); if (AC_FILE == NULL) { printf("AC_FILE file open failed"); } /* end */ ... /* add by zx */ fclose(AC_FILE); fclose(DC_FILE); /* end */ ...}
tinyjpeg.c
static void decode_MCU_1x1_3planes(struct jdec_private *priv){ // Y process_Huffman_data_unit(priv, cY); /* add by zx */ //以1x1为例,其余的也照此添加 output_DC_AC(priv); /* end */ 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);}
void tinyjpeg_free(struct jdec_private *priv){ ... /* add by zx */ if (priv->AC_Image) free(priv->AC_Image); if (priv->DC_Image) free(priv->DC_Image); /* end */ free(priv);}
int tinyjpeg_decode(struct jdec_private *priv, int pixfmt){ ... /* add by zx */ 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); /* end */ ... /* add by zx */ mapping_DC_AC(priv); /* end */ ...}
/* add by zx */static void output_DC_AC(struct jdec_private *priv){ static long int i = 0; //因为宏块为8*8 因此DC图像应该为实际图像大小除以64 if (i < priv->height*priv->width / 64) { priv->DC_Image[i] = priv->component_infos[0].DCT[0]; priv->AC_Image[i] = priv->component_infos[0].DCT[1]; } i++;}static void mapping_DC_AC(struct jdec_private *priv){ int i; short int min_AC, max_AC, min_DC, max_DC, size; unsigned char *temp_AC, *temp_DC; size = priv->height*priv->width / 64; temp_AC = (unsigned char*)malloc(sizeof(unsigned char)*size); temp_DC = (unsigned char*)malloc(sizeof(unsigned char)*size); //因为AC分量有负值,因此将其归至0-255需要对应映射 //原来DC分量的范围为0-8*255,如果将其归至0-255直接除以8即可 //但是y分量在操作时有-128 因此DC分量变得也有负值,因此也和AC分量一样进行对应映射 min_AC = priv->AC_Image[0]; max_AC = priv->AC_Image[0]; min_DC = priv->DC_Image[0]; max_DC = priv->DC_Image[0]; //寻找AC及DC分量中的最大最小值 for (i = 0; i < size; i++) { if (priv->AC_Image[i] < min_AC) min_AC = priv->AC_Image[i]; if (priv->AC_Image[i] > max_AC) max_AC = priv->AC_Image[i]; if (priv->DC_Image[i] < min_DC) min_DC = priv->DC_Image[i]; if (priv->DC_Image[i] > max_DC) max_DC = priv->DC_Image[i]; } //进行映射 for (i = 0; i < size; i++) { temp_AC[i] = (unsigned char)(255 * (priv->AC_Image[i] - min_AC) / (max_AC - min_AC)); temp_DC[i] = (unsigned char)(255 * (priv->DC_Image[i] - min_DC) / (max_DC - min_DC)); } fwrite(temp_AC, 1, size, AC_FILE); fwrite(temp_DC, 1, size, DC_FILE); free(temp_AC); free(temp_DC);}/* end */
三、实验结果
1、以txt文件输出所有的量化矩阵和所有的HUFFMAN码表
2、DC\AC图像输出及其概率分布
- 数据压缩原理实验5_JPEG编解码原理及代码分析
- 实验报告5_JPEG编解码原理及程序调试
- 数据压缩原理实验4_DPCM编解码
- 数据压缩原理实验3_Huffman编解码算法实现与压缩效率分析
- 数据压缩原理 实验三 Huffman编解码算法实现与压缩效率分析
- WebRTC源码分析3_jpeg编解码
- WebRTC源码分析3_jpeg编解码
- WebRTC源码分析3_jpeg编解码
- 数据压缩实验三--Huffman编解码及压缩率的比较
- Base64编、解码原理,及代码实现(一)
- Base64编、解码原理,及代码实现(二)
- 【实验四】无损数据压缩编解码实验
- 数据压缩原理实验5_实验报告
- 数据压缩原理与应用 实验五 JPEG 原理分析及 JPEG 解码器的调试
- 数据压缩原理 实验五 JPEG原理分析及JPEG解码器的调试
- 数据压缩原理与应用 实验三 Huffman编码与解码
- 数据压缩实验三:Huffman编解码
- 数据压缩实验五 JPEG原理分析及JPEG解码器的调试
- 解析线程池
- 总结常见的ES6新语法特性
- socket服务器
- iOS-支付宝&微信支付
- Pandas标记删除重复记录
- 数据压缩原理实验5_JPEG编解码原理及代码分析
- ASP.NET-Cookie对象
- ssl证书多少钱 ?
- This version of Android Studio is incompatible with the Gradle Plugin used.
- PHP Closure类详解
- View.post和View.postDelayed
- 【贪心策略】渡河(river)
- 这才是打开软件品质保证工程师(SQA)职责的正确姿势
- 定时任务调度