数据压缩原理与应用 JPEG解码
来源:互联网 发布:seo一专员 编辑:程序博客网 时间:2024/05/22 07:51
一、实验原理
1. JPEG编码原理
- DCT变换:能量守恒,能量集中,去相关
从框图可以看出先进行电平偏移再进行8x8块的DCT变换。level offset的原因在于Y的范围为(0,255),而U、V的范围为(-128,128),所以Y的电平应该减去128,再分为8x8的块进行DCT变换。
DCT变换的基本思路是将图像分解为8×8的子块或16×16的子块,并对每一个子块进行单独的DCT变换,然后对变换结果进行量化、编码。随着子块尺寸的增加,算法的复杂度急剧上升,因此,实用中通常采用8×8的子块进行变换。
对每个单独的彩色图像分量,把整个分量图像分成8×8的图像块,如图所示,并作为两维离散余弦变换。
- 基于人眼特性量化DCT系数
选择的是中平型均匀量化器,因为人眼对亮度信号比对色差信号更敏感,因此使用了两种量化表:亮度量化值和色差量化值。
根据人眼的视觉特性(对低频敏感,对高频不太敏感)对低频分量采取较细的量化,对高频分量采取较粗的量化。如果原始图象中细节丰富,则去掉的数据较多,量化后的系数与量化前差别较大。
- 对量化后的DCT系数F(u,v)数据进行熵编码
DC系数编码
由于直流系数 F(0,0)反映了该子图像中包含的直流成分,通常较大,又由于两个相邻的子图像的直流系数通常具有较大的相关性,所以对 DC 系数采用差值脉冲编码(DPCM),即对本像素块直流系数与前一像素块直流系数的差值进行无损编码。
AC系数编码
AC编码进行了之字形扫描:由于经DCT变换后,系数大多数集中在左上角,即低频分量区,因此采用Z字形按频率的高低顺序读出,可以出现很多连零的机会。可以使用游程编码。尤其在最后,如果都是零,给出EOB (End of Block)即可。
游程编码:系数序列分组,将非零系数和它前面的相邻的全部零系数分在一组内;每组用两个符号表示[(Run,Size),(Amplitude)],Amplitude:表示非零系数的幅度值;Run:表示零的游程即零的个数;Size:表示非零系数的幅度值的编码位数;
2. JPEG解码
与编码相反
3. JPEG文件格式
DQT 定义量化表
DHT,定义哈夫曼表
二、实验流程
三、关键代码分析
- 将输出文件保存为可供YUVViewer观看的YUV文件
tinyjpeg.h
enum tinyjpeg_fmt { TINYJPEG_FMT_GREY = 1, TINYJPEG_FMT_BGR24, TINYJPEG_FMT_RGB24, TINYJPEG_FMT_YUV420P, TINYJPEG_FMT_YUV420ALL,//////////////////////////////////edit by lee 2017 5 16 };
loadjpeg.c
int main(int argc, char *argv[]) { int output_format = TINYJPEG_FMT_YUV420ALL;//将输出的格式换为yuv文件格式 ... 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 if (strcmp(argv[current_argument + 1], "yuv420all") == 0)//////add yuv file 2017 5 16 bu lee output_format = TINYJPEG_FMT_YUV420ALL; else exitmessage("Bad format: need to be one of yuv420p, rgb24, bgr24, grey\n"); ... }
int load_multiple_times(const char *filename, const char *outfilename, int output_format) { ...... /* 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); case TINYJPEG_FMT_YUV420ALL:// write_pgm(outfilename, width, height, components);//add yuv file 2017 5 16 by lee break; } ...... }
int convert_one_image(const char *infilename, const char *outfilename, int output_format) { ...... /* 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: /////////////////////////////////////////you can create yourself///////////////////////////////// write_yuv(outfilename, width, height, components); break; case TINYJPEG_FMT_GREY: write_pgm(outfilename, width, height, components); case TINYJPEG_FMT_YUV420ALL: write_yuv_file(outfilename, width, height, components);/////////add by lee break; } }
int tinyjpeg_decode(struct jdec_private *priv, int pixfmt) { ...... switch (pixfmt) { case TINYJPEG_FMT_YUV420ALL://add by lee case TINYJPEG_FMT_YUV420P: 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; ...... }
输出yuv文件
static void write_yuv_file(const char *filename, int width, int height, unsigned char **components)//output yuv file 2017 5 16 by lee { 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); }
- 以txt文件输出所有的量化矩阵和所有的HUFFMAN码表
tinyjpeg.h
#define QTXT 1//add by lee 2017 5 16 #define QTXTFILE "quan_jpeg.txt"//add by lee 2017 5 16
tinyjpeg.c
输出huffman码表
static void build_huffman_table(const unsigned char *bits, const unsigned char *vals, struct huffman_table *table) { ...... #if TRACE fprintf(p_trace,"val=%2.2x code=%8.8x codesize=%2.2d\n", val, code, code_size); fflush(p_trace); #endif #if QTXT fprintf(quan, "val=%2.2x code=%8.8x codesize=%2.2d\n", val, code, code_size); fflush(quan); #endif ...... }
输出量化码表
static void build_quantization_table(float *qtable, const unsigned char *ref_table) { ...... for (i=0; i<8; i++) { for (j=0; j<8; j++) { #if QTXT////////////////////edit by lee 2017 5 16 fprintf(quan,"%d ",ref_table[*zz]); //ref_table[*zz]存的即为量化码表,直接输出 fflush(quan); #endif *qtable++ = ref_table[*zz++] * aanscalefactor[i] * aanscalefactor[j]; } #if QTXT/////////////////////////////////edit by lee 2017 5 16 fprintf(quan,"\n"); fflush(quan); #endif } ...... }
输出txt文件中DQT码表的头尾
static int parse_DQT(struct jdec_private *priv, const unsigned char *stream) { ...... #if TRACE fprintf(p_trace,"> DQT marker\n"); fflush(p_trace); #endif #if QTXT////////////////////edit by lee 2017 5 16 fprintf(quan, "> DQT marker\n"); fflush(quan); #endif #if TRACE fprintf(p_trace,"< DQT marker\n"); fflush(p_trace); #endif #if QTXT//edit by lee 2017 5 16 fprintf(quan, "< DQT marker\n"); fflush(quan); #endif ...... }
- 输出DC图像和某一个AC值图像
tinyjpeg.h
void DCiamge(struct jdec_private *priv);//存储DC系数 void ACiamge(struct jdec_private *priv);//存储AC系数 void outputDCAC(struct jdec_private *priv);//输出图像
tinyjpeg.c
开空间以及调用函数
int tinyjpeg_decode(struct jdec_private *priv, int pixfmt) { .................................................. priv->DCImage = (int *)malloc(sizeof(int)*priv->width * priv->height/64);//edit by lee 2017 5 18 priv->ACImage = (int *)malloc(sizeof(int)*priv->width * priv->height/64);//edit by lee 2017 5 18 outputDCAC(priv);//edit by lee 2017 5 20 ....................................... }
/* * output DC * edit by lee 2017 5 20 */ static void DCiamge(struct jdec_private *priv) { static long i = 0;//i表示图像中DCT块的个数 if (i<priv->height*priv->width / 64)//8x8的块 { priv->DCImage[i] = priv->component_infos[cY].DCT[0];//cY=0 } i++; }
/* * output AC * edit by lee 2017 5 20 */ static void ACiamge(struct jdec_private *priv) { static long int i = 0; if (i<priv->height*priv->width / 64) priv->ACImage[i] = priv->component_infos[cY].DCT[2];//此处选取的是DCT[64]的第二个ac系数,而其实取值为(1,63)皆可 i++; }
/* * output DCACimage * edit by lee 2017 5 20 */ static void outputDCAC(struct jdec_private *priv) { int i = 0; int dcmax, dcmin;//记录下DC值中最大最小值方便归一化 int acmax, acmin;//记录下AC值中最大最小值方便归一化 //此处归一化有两种方法,一种全部除以8,一种用最大最小值归一化,此处选择后者,保证比例准确 unsigned char *temp;//记录归一化后的值方便输出 /*设置初值*/ dcmin = priv->DCImage[0]; dcmax = priv->DCImage[0]; acmin = priv->ACImage[0]; acmax = priv->ACImage[0]; temp = (unsigned char*)malloc(priv->height*priv->width / 64);//找出最大最小值并临时存放值 for (i = 0; i < (priv->height*priv->width / 64); i++) { if (priv->DCImage[i] > dcmax) dcmax = priv->DCImage[i]; if (priv->DCImage[i] < dcmin) dcmin = priv->DCImage[i]; if (priv->ACImage[i] > acmax) acmax = priv->ACImage[i]; if (priv->ACImage[i] < acmin) acmin = priv->ACImage[i]; }//循环找出最大最小值 for (i = 0; i < (priv->height*priv->width / 64); i++) { temp[i] = (unsigned char) 255*(priv->DCImage[i] - dcmin) / (dcmax - dcmin); } fwrite(temp, 1, priv->width*priv->height / 64, DFILE);//DC系数以及写出dc系数进DFILE for (i = 0; i < (priv->height*priv->width / 64); i++) { temp[i] = (unsigned char)255*(priv->ACImage[i] - acmin)/ (acmax - acmin); } fwrite(temp, 1, priv->width*priv->height / 64, AFILE);//DC系数以及写出dc系数进DFILE if (temp) free(temp); }
类似的函数都需要在Y分量中加入DCiamge和ACiamge函数
static void decode_MCU_1x1_1plane(struct jdec_private *priv) { // Y process_Huffman_data_unit(priv, cY); DCiamge(priv);//edit by lee 2017 5 20 ACiamge(priv);//edit by lee 2017 5 20 IDCT(&priv->component_infos[cY], priv->Y, 8); ........ }
下省略
四、实验结果
DQT表和huffman编码码表
DC系数的YUV图像和AC系数之一的图像
- 蚊子噪声
高频锐利截止,因为变换后的高频系数很小,量化后值为0,高频截止,导致蚊子噪声。
原始图片:
压缩后的图像
可以看出压缩比越大,蚊子噪声越明显。
- 数据压缩原理与应用 JPEG解码
- 数据压缩原理与应用 实验五 JPEG 原理分析及 JPEG 解码器的调试
- 数据压缩原理与应用 实验三 Huffman编码与解码
- 数据压缩 JPEG解码
- 【数据压缩】JPEG编解码
- 【数据压缩】Exp05.JPEG解码
- 【数据压缩】JPEG标准与原理解析
- 数据压缩实验五:JPEG解码
- 数据压缩实验五:JPEG解码
- 数据压缩实验无--jpeg解码
- 数据压缩原理与应用 BMP转YUV
- 数据压缩原理与应用 Huffman编码
- 数据压缩原理与应用 DPCM编码
- 数据压缩原理与应用 MPEG音频编码
- 数据压缩原理与应用 H.264编码
- JPEG编解码原理
- JPEG解码原理详解
- 【数据压缩】JPEG原理分析及JPEG解码器的调试
- iOS 高质量编程技巧
- Python3.5 内置函数大全
- URLParser:很实用的URL参数解析器(JAVA代码中方便获取QueryString中的get参数)
- 【学习】视觉SLAM资源集锦
- [Leetcode]_29 Divide Two Integers
- 数据压缩原理与应用 JPEG解码
- MySQL Internals Manual
- MYSQL的C API之mysql_query
- USACO-Section1.1 Greedy Gift Givers
- Android 基本优化(一)
- Codeforces 797E Array Queries
- Http 206 文件断点续传下载原理
- A. 这是一道简单的水题~
- HYSBZ 1831 逆序对