【实验五】JPEG解码
2017-06-14 22:44
113 查看
一、实验原理
1.JPEG简介:
JPEG是Joint PhotographicExperts Group(联合图像专家小组)的缩写,是第一个国际图像压缩标准。JPEG图像压缩算法能够在提供良好的压缩性能的同时,具有比较好的重建质量,被广泛应用于图像、视频处理领域。
根据人眼的视觉特性,人眼对亮度信息比色度信息敏感,对低频信息比高频信息敏感。首先对色度信息进行下采样。另外,将空间域的亮度色度信息经DCT变换到频域,对亮度信息细量化、色彩信息粗量化,对低频信息细量化、高频信息粗量化。再对量化结果进行变长编码,达到压缩图片的目的,同时主观上图片质量不甚下降。
2.JPEG编码:
![](https://img-blog.csdn.net/20170614224619929?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQveWVqaWExMjgw/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
下采样:为实现压缩,对色度信息进行下采样,由原4:4:4的格式变为4:2:2或4:2:0格式或不进行下采样。
分块:将图像分成(8*8)的块以便进行DCT变换,不够的要取边缘像素补齐
零偏置:零偏置,对于灰度级是2n的像素,通过减去2n-1,将无符号的整数值变成有符号数,对于n=8,即将0~255的值域,通过减去128,转换为值域在-128~127之间的值,使像素的绝对值出现3位10进制的概率大大减少。
DCT:经过DCT变换后,图像中的低频分量会集中在左上角,由于图像低频能量高,故而左上角数值大,右下角有较多的0值。
量化:人眼对低频敏感,对高频不敏感。所以低频细量化,高频粗量化;人眼对亮度信号敏感,对色度信号不敏感。所以亮度和色度分量分别采用不同的量化表。
之字形扫描:游程编码的扫描过程,由于DCT变换后,越往右下角的分量越小甚至为0,于是采用之字形扫描。将二维降到一维后会在高频部分出现连0,由此可以提高编码效率。
熵编码:利用相邻块之间DC系数的空间相关性,对DC系数进行DPCM编码,即当前块的DC系数减去前一块的DC系数后编码。对AC系数进行游程编码(Run-Length Ecoding)。
3.JPEG解码
JPEG 在文件中以Segment
的形式组织,它具有以下特点:
均以0xFF开始,后跟1byte的Marker和2byte的Segmentlength(包含表示Length本身所占用的2byte,不含”0xFF”+”Marker”所占用的2byte)
采用Motorola序(相对于Intel序),即保存时高位在前,低位在后。
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)理解struct huffman_table、struct component、struct jdec_private三个结构体的设计目的:
jdec_private:用于存储JPEG的基本信息,如图像宽高、数据流指针、量化表、霍夫曼码表、IDCT后的亮色通道值等等等诸多信息。包含component结构体和huffman_table结构体
component:解码时,该结构体来储存当前宏块的所用解码信息,经解码后的8x8DCT系数矩阵。
huffman_table:存放重建后的Huffman码表。
(2) 将输出文件保存为可供YUVViewer观看的YUV文件。
static void write_yuv(const char *filename, int width, int height, unsigned char **components)
{
FILE *F,*acfile,*dcfile;
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);
acfile = fopen(acfilename, "wb");
dcfile = fopen(dcfilename, "wb");
fwrite(outDCbuf, width * height *3 /64 , 1, dcfile);//直流分量文件
fwrite(outACbuf, width * height *3 /64 , 1, acfile);//交流分量文件
fclose(acfile);
fclose(acfile);
}
(3)
(4)提取量化表。
(5)提取Huffman表。在build_huffman_table函数中仿照了TRACE直接在函数中将val,code和codesize输出到了txt中。
(6)提取DC和AC系数输出图像。
具体数值从component结构体里的DCT中直接提取的。写U,V直接在Y的基础上分别加上1倍和2倍图像size的办法找到对应的位置。重建DC、AC系数后,其值不在0~255范围内,所以需要将它的值映射到0~255范围内,再作为亮度信息写入输出的YUV文件。
(1)输出图像直流dc.yuv 交流ac.yuv
![](https://img-blog.csdn.net/20170614224627752?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQveWVqaWExMjgw/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
(2)table.txt
![](https://img-blog.csdn.net/20170614224636414?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQveWVqaWExMjgw/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
![](https://img-blog.csdn.net/20170614224642658?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQveWVqaWExMjgw/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
(3)概率分布
![](https://img-blog.csdn.net/20170614224648424?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQveWVqaWExMjgw/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
1.JPEG简介:
JPEG是Joint PhotographicExperts Group(联合图像专家小组)的缩写,是第一个国际图像压缩标准。JPEG图像压缩算法能够在提供良好的压缩性能的同时,具有比较好的重建质量,被广泛应用于图像、视频处理领域。
根据人眼的视觉特性,人眼对亮度信息比色度信息敏感,对低频信息比高频信息敏感。首先对色度信息进行下采样。另外,将空间域的亮度色度信息经DCT变换到频域,对亮度信息细量化、色彩信息粗量化,对低频信息细量化、高频信息粗量化。再对量化结果进行变长编码,达到压缩图片的目的,同时主观上图片质量不甚下降。
2.JPEG编码:
下采样:为实现压缩,对色度信息进行下采样,由原4:4:4的格式变为4:2:2或4:2:0格式或不进行下采样。
分块:将图像分成(8*8)的块以便进行DCT变换,不够的要取边缘像素补齐
零偏置:零偏置,对于灰度级是2n的像素,通过减去2n-1,将无符号的整数值变成有符号数,对于n=8,即将0~255的值域,通过减去128,转换为值域在-128~127之间的值,使像素的绝对值出现3位10进制的概率大大减少。
DCT:经过DCT变换后,图像中的低频分量会集中在左上角,由于图像低频能量高,故而左上角数值大,右下角有较多的0值。
量化:人眼对低频敏感,对高频不敏感。所以低频细量化,高频粗量化;人眼对亮度信号敏感,对色度信号不敏感。所以亮度和色度分量分别采用不同的量化表。
之字形扫描:游程编码的扫描过程,由于DCT变换后,越往右下角的分量越小甚至为0,于是采用之字形扫描。将二维降到一维后会在高频部分出现连0,由此可以提高编码效率。
熵编码:利用相邻块之间DC系数的空间相关性,对DC系数进行DPCM编码,即当前块的DC系数减去前一块的DC系数后编码。对AC系数进行游程编码(Run-Length Ecoding)。
3.JPEG解码
JPEG 在文件中以Segment
的形式组织,它具有以下特点:
均以0xFF开始,后跟1byte的Marker和2byte的Segmentlength(包含表示Length本身所占用的2byte,不含”0xFF”+”Marker”所占用的2byte)
采用Motorola序(相对于Intel序),即保存时高位在前,低位在后。
JPEG图像格式介绍:
缩写 | 名称 | 说明 | 标记代码 | 字节数 |
SOI | Start of Image | 图像开始 | 固定值0xFFD8 | 2(标记代码) |
EOI | End of Image | 图像结束 | 固定值0xFFD9 | 2(标记代码) |
APP0 | Application | 应用程序保留标记 | 固定值0xFFE0 | variable |
DQT | Define Quantization Table | 定义量化表 | 固定值0xFFDB | variable |
SOF0 | Start of Frame | 帧图像开始 | 固定值0xFFC0 | variable |
DHT | Define Huffman Table | 定义哈夫曼表 | 固定值0xFFC4 | variable |
SOS | Start of Scan | 扫描开始 | 固定值0xFFDA | variable |
(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)理解struct huffman_table、struct component、struct jdec_private三个结构体的设计目的:
jdec_private:用于存储JPEG的基本信息,如图像宽高、数据流指针、量化表、霍夫曼码表、IDCT后的亮色通道值等等等诸多信息。包含component结构体和huffman_table结构体
component:解码时,该结构体来储存当前宏块的所用解码信息,经解码后的8x8DCT系数矩阵。
huffman_table:存放重建后的Huffman码表。
(2) 将输出文件保存为可供YUVViewer观看的YUV文件。
static void write_yuv(const char *filename, int width, int height, unsigned char **components)
{
FILE *F,*acfile,*dcfile;
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);
acfile = fopen(acfilename, "wb");
dcfile = fopen(dcfilename, "wb");
fwrite(outDCbuf, width * height *3 /64 , 1, dcfile);//直流分量文件
fwrite(outACbuf, width * height *3 /64 , 1, acfile);//交流分量文件
fclose(acfile);
fclose(acfile);
}
(3)
/*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图像开缓冲区 #define ACNUM 2//提取的AC图像所在的DCT的位置索引 #define C_TABLE 1 #define C_FILENAME "table.txt" #define acfilename "ac.yuv" #define dcfilename "dc.yuv" /*main*/ #if C_TABLE c_file = fopen(C_FILENAME,"w"); if(c_file == NULL) { printf("table file open error!"); } #endif
(4)提取量化表。
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)]); //zz按照zigzag索引 fprintf(c_file,"%s\t",out_str); if(j == 7) fprintf(c_file,"\n\n"); fflush(c_file); #endif
(5)提取Huffman表。在build_huffman_table函数中仿照了TRACE直接在函数中将val,code和codesize输出到了txt中。
#if C_TABLE fprintf(c_file,"val=%2.2x code=%8.8x codesize=%2.2d\n", val, code, code_size); fflush(c_file); #endif #if C_TABLE fprintf(c_file,"\n\n"); fflush(c_file); #endif
(6)提取DC和AC系数输出图像。
具体数值从component结构体里的DCT中直接提取的。写U,V直接在Y的基础上分别加上1倍和2倍图像size的办法找到对应的位置。重建DC、AC系数后,其值不在0~255范围内,所以需要将它的值映射到0~255范围内,再作为亮度信息写入输出的YUV文件。
/*tinyjpeg_decode*/ //DC *(outDCbuf + dct_count) = (unsigned char)((priv->component_infos[cY]).DCT[0]/8 +128); *(outDCbuf + dct_count + priv->height * priv->width /64) = (unsigned char)((priv->component_infos[cCb].DCT[0])/8 + 128) ;//U *(outDCbuf + dct_count + 2 * priv->height * priv->width /64) = (unsigned char)((priv->component_infos[cCr].DCT[0])/8 + 128) ;//V //AC: *(outACbuf + dct_count) = (unsigned char)((priv->component_infos[cY]).DCT[ACNUM] +128 ); *(outACbuf + dct_count + priv->height * priv->width /64) = (unsigned char)((priv->component_infos[cCb]).DCT[ACNUM] +128 ); *(outACbuf + dct_count + 2 * priv->height * priv->width /64) = (unsigned char)((priv->component_infos[cCr]).DCT[ACNUM] +128 );三、实验结论
(1)输出图像直流dc.yuv 交流ac.yuv
(2)table.txt
(3)概率分布
相关文章推荐
- 《数据压缩》实验报告五·JPEG编解码
- 数据压缩实验五:JPEG文件解码实验分析
- 实验五——JPEG编解码
- 实验五 JPEG解码
- 数据压缩原理实验5_JPEG编解码原理及代码分析
- 实验报告5_JPEG编解码原理及程序调试
- 数据压缩实验无--jpeg解码
- 图像解码之一——使用libjpeg解码jpeg图片
- 【数据压缩】JPEG编解码
- JPEG编解码过程详解
- Jpeg 库的解码OpenCL优化
- JPEG文件编/解码详解
- Tiny Jpeg Decoder (JPEG解码程序) 源代码分析 2:解码数据
- 基于DSP的jpeg图像解码算法的实现
- 基于CUDA的JPEG解码
- 使用libjpeg解码jpeg图片
- jpeg图像的压缩与解码
- Tiny Jpeg Decoder (JPEG解码程序) 源代码分析 2:解码数据
- Jpeglib移植到arm平台 & 修改支持解码内存中的jpeg数据流
- 实验: 文件名上传时的编码和下载后的解码