实验报告5_JPEG编解码原理及程序调试
2017-06-05 21:54
447 查看
一.实验原理
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.henum tinyjpeg_fmt{ TINYJPEG_FMT_GREY = 1, TINYJPEG_FMT_BGR24, TINYJPEG_FMT_RGB24, TINYJPEG_FMT_YUV420P, TINYJPEG_FMT_YUV,//add by hyw };loadjpeg.c
main函数中:
…… 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, widt bc0a h, 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.hvoid 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++; }AC
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++; }
输出图像
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); }
四.实验结果
相关文章推荐
- 《数据压缩》实验报告五·JPEG编解码
- 数据压缩原理与应用 实验五 JPEG 原理分析及 JPEG 解码器的调试
- 数据压缩原理实验5_JPEG编解码原理及代码分析
- 数据压缩原理 实验五 JPEG原理分析及JPEG解码器的调试
- 《第三周实验报告2-1》---找出错误调试程序
- 数据压缩 实验五 JPEG原理分析 JPEG解码器的调试
- 红外遥控系统原理及单片机软件解码程序,我的编写经历(C版本)
- 凌阳SPCE3200精简开发板:解决附带实验程序中printf无法输出串口调试日志信息的问题
- 数据挖掘实验报告-决策树程序实验
- 使用C/C++实现Socket聊天程序(代码+实验报告)
- 简单的JPEG解码程序
- c#实验报告模拟发牌程序
- web实验报告——会话跟踪使用程序
- JXARM9-2410系列教学实验系统GDB调试程序流程
- 基于网络环境的程序设计综合实验报告
- 实验5-7 程序调试入门
- web实验报告——会话跟踪使用程序
- 实验五: 编写、调试具有多个段的程序
- 计算机网络原理实验报告(一)
- 汇编语言 实验5 编写,调试具有多个段的程序(转载)