实验报告5_JPEG编解码原理及程序调试
来源:互联网 发布:汉诺塔的非递归算法 编辑:程序博客网 时间:2024/06/16 17:04
一.实验原理
JPEG是一种有损压缩格式,能够将图像压缩在很小的储存空间,图像中重复或不重要的资料会被丢失,因此容易造成图像数据的损伤。JPEG图像压缩算法能够在提供良好的压缩性能的同时,具有比较好的重建质量,被广泛应用于图像、视频处理领域。
JPEG编码流程:
JPEG编解码流程:
1.编码过程详解:
(1)零偏置:
对于灰度级是2n的像素,通过减去2n-1,将无符号的整数值变成有符号数。
eg.对于n=8,即将0~255的值域,通过减去128,转换为值域在-128~127之间的值
目的:使像素的绝对值出现3位10进制的概率大大减少。
(2)8x8DCT变换
将图像分为8x8的子块,边缘不满足8x8的部分则用边缘像素填充,然后对每一个子块进行DCT变换。
DCT变换公式:
(3)对DCT系数进行量化
量化采用中平型均匀量化器,量化步距是按照系数所在位置和颜色分量来确定。
因为人眼对亮度信号比对色差信号更敏感,因此使用了两种量化表:亮度量化值和色差量化值
根据人眼的视觉特性(对低频敏感,对高频不太敏感)对低频分量采取较细的量化,对高频分量采取较粗的量化
(4)编码
DC编码:
8×8图像块经过DCT变换之后得到的DC直流系数有两个特点
-系数的数值比较大
-相邻8×8图像块的DC系数值变化不大:冗余
根据这个特点,JPEG算法使用了差分脉冲调制编码(DPCM)技术,对相邻图像块之间量化DC系数的差值DIFF进行编码:
对DIFF用Huffman编码:分成类别
-类别ID:一元码编码
-类内索引:采用定长码
AC编码(游程编码):
由于经DCT变换后,系数大多数集中在左上角,即低频分量区,因此采用Z字形按频率的高低顺序读出,可以出现很多连零的机会。可以使用游程编码。尤其在最后,如果都是零,给出EOB (End of Block)即可。
在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编码
-对类内索引用定长码编码
2.JPEG数据格式
JPEG语法结构中定义了一系列标记(Markers)
-均以0xFF开始,后跟1字节的标记标识符和2字节的标记长度以及该标记所对应的payload
-标记长度部分高位在前,低位在后,不包含该标记的头两个字节
-熵编码部分的数据在0xFF后由编码器插入0x00,解码器解码时跳过此字节不予处理
-SOI( Start Of Image)和EOI( End Of Image)标记没有payload
可在下例中找到对应:
二.实验流程
(1)逐步调试JPEG解码器程序。将输入的JPG文件进行解码,将输出文件保存为可供YUVViewer观看的YUV文件。
(2)理解程序设计的整体框架
(3)理解三个结构体的设计目的:struct huffman_table, struct component , struct jdec_private
(4)理解在视音频编码调试中 TRACE TRACE的目和含义
-会打开和关闭 TRACE TRACE
-会根据自己的要求修改 TRACE TRACE
(5)以txt文件输出所有的量化矩阵和所有的HUFFMAN码表。
(6)输出DC图像并经过huffman统计其概率分布(使用第三个实验中的 Huffman Huffman Huffman编码器)。
(7)输出某一个 AC 值图像并统计其概率分布(使用第三个实验中的 Huffman HuffmanHuffman 编码器)。
三.关键代码分析
1.将输出文件保存成YUV格式
tinyjpeg.h
enum tinyjpeg_fmt{ TINYJPEG_FMT_GREY = 1, TINYJPEG_FMT_BGR24, TINYJPEG_FMT_RGB24, TINYJPEG_FMT_YUV420P, TINYJPEG_FMT_YUV,//add by hyw};
loadjpeg.cmain函数中:
……int output_format = TINYJPEG_FMT_YUV;//将输出格式设置为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], "yuv") == 0)//add by hyw output_format = TINYJPEG_FMT_YUV; else exitmessage("Bad format: need to be one of yuv420p, rgb24, bgr24, grey\n"); output_filename = argv[current_argument+2];
int load_multiple_times(const char *filename, 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; case TINYJPEG_FMT_GREY: write_pgm(outfilename, width, height, components); break; case TINYJPEG_FMT_YUV: write_pgm(outfilename, width, height, components);//add by hyw break; }……}
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; case TINYJPEG_FMT_GREY: write_pgm(outfilename, width, height, components); break; case TINYJPEG_FMT_YUV: write_yuv_file(outfilename, width, height, components);//add by hyw break; } ……}
int tinyjpeg_decode(struct jdec_private *priv, int pixfmt){……switch (pixfmt) { case TINYJPEG_FMT_YUV420://add by hyw 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_yuv2(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],1,width* height, F); fwrite(components[1], 1, width* height/4, F); fwrite(components[2], 1, width* height/4, F);}
2.将量化矩阵和Huffman表输出到txt文件
tinyjpeg.h
#define RTXT 1//add by hyw#define RTXTFILE "result_jpeg.txt"//add by hyw
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 RTXT fprintf(p_rtxt, "val=%2.2x code=%8.8x codesize=%2.2d\n", val, code, code_size); fflush(p_rtxt); #endif ...... }
输出量化表:
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++) {#if RTXT fprintf(p_rtxt, "%d ", ref_table[*zz]);//输出的量化矩阵为之字形扫描顺序 fflush(p_rtxt); #endif*qtable++ = ref_table[*zz++] * aanscalefactor[i] * aanscalefactor[j]; } } #if RTXT
fprintf(p_rtxt, "\n");
fflush(p_rtxt);
#endif}DQT码表头尾:static int parse_DQT(struct jdec_private *priv, const unsigned char *stream){ int qi; float *table; const unsigned char *dqt_block_end;#if TRACE fprintf(p_trace,"> DQT marker\n"); fflush(p_trace);#endif#if RTXT//add by hyw fprintf(p_rtxt, "> DQT marker\n"); fflush(p_rtxt);#endif dqt_block_end = stream + be16_to_cpu(stream); stream += 2;/* Skip length */ while (stream < dqt_block_end) { 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 = priv->Q_tables[qi]; build_quantization_table(table, stream); stream += 64; }#if TRACE fprintf(p_trace,"< DQT marker\n"); fflush(p_trace);#endif#if RTXT//add by hyw fprintf(p_rtxt, "< DQT marker\n"); fflush(p_rtxt);#endif return 0;}3.输出DC图像和某一AC的图像:
tinyjpeg.h
void DC(struct jdec_private *priv);//DC void AC(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);//by hyw ....................................... }DC
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++; }
ACstatic 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++; }输出图像
static void outputDCAC(struct jdec_private *priv) { int i = 0; int dcmax, dcmin;//方便归一化 int acmax, acmin;//方便归一化 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); 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); if (temp) free(temp); }四.实验结果
- 实验报告5_JPEG编解码原理及程序调试
- 数据压缩原理实验5_JPEG编解码原理及代码分析
- WebRTC源码分析3_jpeg编解码
- WebRTC源码分析3_jpeg编解码
- WebRTC源码分析3_jpeg编解码
- 数据压缩原理实验4_DPCM编解码
- 《数据压缩》实验报告四·DPCM编解码
- 《数据压缩》实验报告五·JPEG编解码
- 数据压缩实验三--Huffman编解码及压缩率的比较
- Base64编解码原理
- 视频编解码原理
- gif编解码原理
- base64编解码原理
- 视频编解码原理
- 视频编解码原理
- MPEG4编解码原理
- JPEG编解码原理
- H264编解码原理
- Android实践:有序广播
- struts jstl标签
- xxxx
- Ubuntu16.04离线安装Nodejs与JDK
- qwb与学姐---之江学院第0届校赛最大生成树+lca
- 实验报告5_JPEG编解码原理及程序调试
- socket编程参考
- 母版页的制作
- 使用DB2优化概要强制修改DB2的执行计划
- 并查集总结
- js导出数据到excel
- 深度强化学习——DQN
- 活动指示器UIActivityIndicatorView
- Intellij Idea不能热部署的解决方法