实验五 JPEG解码
来源:互联网 发布:软件企业资质 编辑:程序博客网 时间:2024/06/05 11:23
实验原理
JPEG简介
JPEG是Joint PhotographicExperts Group(联合图像专家小组)的缩写,是第一个国际图像压缩标准。JPEG图像压缩算法能够在提供良好的压缩性能的同时,具有比较好的重建质量,被广泛应用于图像、视频处理领域。
JPEG 在文件中以 Segment 的形式组织,它具有以下特点:
1.均以 0xFF 开始,后跟 1 byte 的 Marker 和 2 byte 的 Segment length(包含表示 Length 本身所占用的 2 byte,不含“0xFF” + “Marker” 所占用的 2 byte);
2.采用 Motorola 序(相对于 Intel 序),即保存时高位在前,低位在后;
3. Data 部分中,0xFF 后若为 0x00,则跳过此字节不予处理;
JPEG编码原理图
零偏置:零偏置,对于灰度级是2n的像素,通过减去2n-1,将无符号的整数值变成有符号数,对于n=8,即将0~255的值域,通过减去128,转换为值域在-128~127之间的值,使像素的绝对值出现3位10进制的概率大大减少。零偏置:零偏置,对于灰度级是2n的像素,通过减去2n-1,将无符号的整数值变成有符号数,对于n=8,即将0~255的值域,通过减去128,转换为值域在-128~127之间的值,使像素的绝对值出现3位10进制的概率大大减少。零偏置:零偏置,对于灰度级是2n的像素,通过减去2n-1,将无符号的整数值变成有符号数,对于n=8,即将0~255的值域,通过减去128,转换为值域在-128~127之间的值,使像素的绝对值出现3位10进制的概率大大减少。
零偏置:
零偏置,对于灰度级是2n的像素,通过减去2n-1,将无符号的整数值变成有符号数,对于n=8,即将0~255的值域,通过减去128,转换为值域在-128~127之间的值,使像素的绝对值出现3位10进制的概率大大减少。
DCT:
经过DCT变换后,图像中的低频分量会集中在左上角,由于图像低频能量高,故而左上角数值大,右下角有较多的0值。
量化:
人眼对低频敏感,对高频不敏感。所以低频细量化,高频粗量化;人眼对亮度信号敏感,对色度信号不敏感。所以亮度和色度分量分别采用不同的量化表。
DC系数差分编码:
之字形扫描:
游程编码的扫描过程,由于DCT变换后,越往右下角的分量越小甚至为0,于是采用之字形扫描。将二维降到一维后会在高频部分出现连0,由此可以提高编码效率。
熵编码:
利用相邻块之间DC系数的空间相关性,对DC系数进行DPCM编码,即当前块的DC系数减去前一块的DC系数后编码。对AC系数进行游程编码(Run-Length Ecoding)。
JPEG图像格式介绍:
解码流程:
(1)读取文件
(2)解析Segment Marker
*解析SOI
*解析APP0
检查标识”JFIF”及版本
得到一些参数
*解析DQT
得到量化表长度(可能包含多张量化表)
得到量化表的精度
得到及检查量化表的序号(只能是0~3)
得到量化表内容(64个数据)
*解析SOF0
得到每个sample的比特数、长宽、颜色分量数
得到每个颜色分量的ID、水平采样因子、垂直采样因子、使用的量化表序号(与DQT中序号对应)
*解析DHT
得到 Huffman表的类型(AC、DC)、序号
依据数据重建 Huffman表
*解析SOS
得到解析每个颜色分量的DC、AC值所使用的Huffman表序号(与DHT中序号对应)(3)依据每个分量的水平、垂直采样因子计算MCU的大小,并得到每个 MCU 中8*8宏块的个数。
(4)对每个MCU解码(依照各分量水平、垂直采样因子对MCU中每个分量宏块解码)。
*对每个宏块进行Huffman解码,得到DCT系数
*对每个宏块的DCT系数进行IDCT,得到Y、Cb、Cr
*遇到 Segment Marker RST时,清空之前的 DC DCT系数
(5)解析到 EOI,解码结束
(6)将Y、Cb、Cr转化为需要的色彩空间并保存。
实验步骤:
1.逐步调试JPEG解码器程序。将输入的JPG文件进行解码,将输出文件保存为可供
YUVViewer观看的YUV文件。
2.程序调试过程中,应做到:理解程序设计的整体框架;理解三个结构体的设计目的;
struct huffman_table struct component struct jdec_private
主要代码分析:
理解struct huffman_table、struct component、struct jdec_private三个结构体的设计目的:
struct huffman_table{ /* 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; struct huffman_table *DC_table; 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]; /*添加两个指针用于存放要输出的DC、AC图像,若要修改具体要输出的 AC分量,则要修改函数“output_AC_Image”*/ short int *DC_Image; short int *AC_Image; jmp_buf jump_state; /* Internal Pointer use for colorspace conversion, do not modify it !!! */ uint8_t *plane[COMPONENTS];};
将输出文件保存为可供YUVViewer观看的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);}
tinyjpeg.h
- FILE *c_file;//输出表格的文件指针
- FILE *acfile ;// AC图像
- FILE *dcfile ;// DC图像
- static char out_str[20];// c_file中输出的字符串
- static unsigned char *outDCbuf = NULL;// 为DC图像开缓冲区
- static unsigned char *outACbuf = NULL; 为AC图像开缓冲区
enum tinyjpeg_fmt { TINYJPEG_FMT_GREY = 1, TINYJPEG_FMT_BGR24, TINYJPEG_FMT_RGB24, TINYJPEG_FMT_YUV420P, TINYJPEG_FMT_YUV, };
loadjpeg.c
int convert_one_image(const char *infilename, const char *outfilename, int output_format){ ...... switch (output_format) { ...... case TINYJPEG_FMT_YUV: write_add_yuv(outfilename, width, height, components); } ......}
提取量化表
static const unsigned char zigzag[64] = { 0, 1, 5, 6, 14, 15, 27, 28, 2, 4, 7, 13, 16, 26, 29, 42, 3, 8, 12, 17, 25, 30, 41, 43, 9, 11, 18, 24, 31, 40, 44, 53, 10, 19, 23, 32, 39, 45, 52, 54, 20, 22, 33, 38, 46, 51, 55, 60, 21, 34, 37, 47, 50, 56, 59, 61, 35, 36, 48, 49, 57, 58, 62, 63 }; /*build_quantization_table*/ //get the Q table #if C_TABLE snprintf(out_str, sizeof(out_str),"%d", ref_table[*(zz-1)]); //按照zigzag索引 fprintf(c_file,"%s\t",out_str); if(j == 7) fprintf(c_file,"\n\n"); fflush(c_file); #endif
以8×8的二维矩阵形式输出量化表,以TAB键为间隔
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 TABLES fprintf(f_tables, "%d\t", ref_table[*zz]); fflush(f_tables); if (j == 7) { fprintf(f_tables,"\n"); fflush(f_tables); }#endif ...... } }}
输出哈夫曼码表:
static void build_huffman_table(const unsigned char *bits, const unsigned char *vals, struct huffman_table *table){ ...... for (i=0; huffsize[i]; i++) { ...... #if TABLES fprintf(f_tables, "val=%2.2x code=%8.8x codesize=%2.2d\n", val, code, code_size); fflush(f_tables); #endif ...... }}
输出DC图像与某一个AC值图像
output_DC_AC_Image用于将AC系数、某一AC系数存放在struct jdec_private的DC_Image和AC_Image空间中,并且根据色度取样格式、MCU的不同有不同的存放方式。
static void output_DC_AC_Image(struct jdec_private *priv,int temp){ static long int i = 0; int row, column; int width; width = priv->width/8; switch ( temp) { case 0://2*2 row = i / (2 * width); column = (i % (2 * width)) / 4; if (i % 4 == 0) { priv->DC_Image[row * 2 * width + column * 2] = priv->component_infos[0].DCT[0]; priv->AC_Image[row * 2 * width + column * 2] = priv->component_infos[0].DCT[1]; } else if (i%4==1) { priv->DC_Image[row * 2 * width + column * 2 + 1] = priv->component_infos[0].DCT[0]; priv->AC_Image[row * 2 * width + column * 2 + 1] = priv->component_infos[0].DCT[1]; } else if (i % 4 == 2) { priv->DC_Image[(row * 2 +1)* width + column * 2] = priv->component_infos[0].DCT[0]; priv->AC_Image[(row * 2 +1)* width + column * 2] = priv->component_infos[0].DCT[1]; } else { priv->DC_Image[(row * 2 + 1) * width + column * 2 + 1] = priv->component_infos[0].DCT[0]; priv->AC_Image[(row * 2 + 1)* width + column * 2 + 1] = priv->component_infos[0].DCT[1]; } break; case 1://1*2 row = i / (2 * width); column = (i % (2 * width)) / 2; if (i % 2 == 0) { priv->DC_Image[row * 2 * width + column ] = priv->component_infos[0].DCT[0]; priv->AC_Image[row * 2 * width + column] = priv->component_infos[0].DCT[1]; } else { priv->DC_Image[(row * 2 + 1)*width + column ] = priv->component_infos[0].DCT[0]; priv->AC_Image[(row * 2 + 1)*width + column ] = priv->component_infos[0].DCT[1]; } break; case 2://2*1 case 3://1*1 priv->DC_Image[i] = priv->component_infos[0].DCT[0]; priv->AC_Image[i] = priv->component_infos[0].DCT[1]; break; default: break; } i++;}
向文件写出DC图像、AC图像
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);}
实验结果分析
输出图像直流dc.yuv 交流ac.yuv
蚊子噪声现象
低压缩比
高压缩比
- 【实验五】JPEG解码
- 实验五:JPEG解码
- 实验五 JPEG解码
- 数据压缩实验五:JPEG解码
- 数据压缩实验五:JPEG解码
- 实验五——JPEG编解码
- 数据压缩实验五:JPEG文件解码实验分析
- 《数据压缩》实验报告五·JPEG编解码
- JPEG解码实验
- 数据压缩实验无--jpeg解码
- 实验五:JPEG编码实验
- 实验五-jpg解码
- 实验五 JPEG原理分析及JPEG 解码器的调试
- 实验五 JPEG原理分析及JPEG解码器的调试
- 实验五 JPEG原理分析及JPEG解码器的调试
- 数据压缩实验五 JPEG原理分析JPEG解码器的调试
- 数据压缩 实验五 JPEG原理分析 JPEG解码器的调试
- 数据压缩 实验五 JPEG原理分析JPEG解码器的调试
- 单应矩阵 基本矩阵 本质矩阵的区别与联系
- xcode反汇编调试iOS模拟器程序(一)查看反汇编
- Java对日期Date类进行加减运算,年份加减,月份加减
- Android 自定义 View 开发
- 机器学习笔记之模型表述
- 实验五 JPEG解码
- 银联商务MISPOS接口开发demo 需要调用POSINF.DLL
- 机器学习笔记二十三 TensorFlow
- 简单转下snowflak的介绍
- vs2010下 LNK1123: 转换到 COFF 期间失败: 文件无效或损坏的问题解决办法
- C++ iterator遍历 以前没注意到的一个点 前置递增和后置递增
- 洛谷P3371 单源最短路径
- 【bitset】POJ2443[Set Operation]题解
- BZOJ 4034 树上操作 链剖