JPEG 是Joint Photographic Experts Group(联合图像专家小组)的缩写,是第一个国际图像压缩标准。JPEG图像压缩算法能够在提供良好的压缩性能的同时,具有比较好的重建质量,被广泛应用于图像、视频处理领域。人们日常碰到的".jpeg"、''.jpg"等指代的是用JPEG算法压缩出来的静态图片文件在媒体上的封存形式,不能与JPEG压缩标准混为一谈。
一、实验内容
1、JPEG编解码原理
JPEG编码的过程如上图所示。解码是编码的逆过程。
2、JPEG文件格式
JPEG 在文件中以 Segment 的形式组织,它具有以下特点:
1.均以 0xFF 开始,即表示图像开始SOI (Start of Image)标记,后跟 1 byte 的 Marker 和 2 byte 的 Segment length(包含表示Length 本身所占用的 2 byte,不含“0xFF” + “Marker” 所占用的 2 byte);
2.采用 Motorola 序(相对于 Intel 序),即保存时高位在前,低位在后;
3.Data 部分中,0xFF 后若为 0x00,则跳过此字节不予处理;
4.一定以0xFFD9结束,表示图像结束EOI(End of Image)标记
(1)APPn,数值0xFF E1~0xFFEF
*N=1~15,数值对应0xE1~0xEF
*APPn长度(Length)
*应用细节信息(Application specific information)
*EXIF文件格式中多写入0xFF E1
(2)DQT标记段,包括一个或者多个量化表
*量化表长度。长度参数占用两个字节,他不包括前两个字节0XFF和0XDB。
*量化表数目。表数目参数占用一个字节,其中高4位为量化表的数据精确度。若高4位等于0,那么后面的量化表中的每个值占8位;若高4位等于1,那么后面的量化表中每个值占16位,低4位表示量化表的编号,为0~3.
*量化表表项。表项参数对应Z字形排列后的64个数值。
(3)DHT标记段,包括一个或多个霍夫曼表
*霍夫曼码表的长度。占用两个字节。
*每个霍夫曼码表(霍夫曼码表一般不止一个,但是绝对不多于4个)都包含4个信息:索引、类型、位表和内容编码。索引信息表示该霍夫曼表的表号,占1个字节。高四位可以是0或1,为0时指DC所用的霍夫曼表;为1时,指AC所用的霍夫曼表。低4位表示霍夫曼表的编号,值为0或1.位表是一个长为16字节的编码,其代码代数和为接下来的编码长度。内容编码信息表示每个霍夫曼码字对应的值
3、JPEG 的解码流程
(1)零偏置(level offset)
对于灰度级是2n的像素,通过减去2n-1,将无符号的整数值变成有符号数
对于n=8,即将0~255的值域,通过减去128,转换为值域在-128~127之间的值
目的:使像素的绝对值出现3位10进制的概率大大减少
(2)8*8DCT变换
对每个单独的彩色图像分量,把整个分量图像分成8×8的图像块,若边缘未满8*8,则用边缘像素进行填充(不建议用黑或白像素填充的原因是可能会破坏图像的原有结构)。
DCT变换使用下式计算:
![](https://img-blog.csdnimg.cn/62786d7f49ed4ae69923c9959569ec1b.png)
图像的低频部分集中在每个8*8块的左上角,高频部分集中在右下角。
(3)量化
因为人眼对亮度信号比对色差信号更敏感,因此使用了两种量化表:亮度量化值和色差量化值;根据人眼的视觉特性(对低频敏感,对高频不太敏感)对低频分量采取较细的量化,对高频分量采取较粗的量化。
(4)DC系数
8×8图像块经过DCT变换之后得到的DC直流系数有两个特点:系数的数值比较大 ,相邻8×8图像块的DC系数值变化不大——有冗余;对此,JPEG算法使用了差分脉冲调制编码(DPCM)技术,对相邻图像块之间量化DC系数的差值DIFF进行编码。
(5)AC系数
由于经过DCT变换后,系数大多集中在左上角,也就是低频分量区,因此采用Z字形扫描,按频率的高低顺序读出,这样会出现很多的连零,可以使用RLE游程编码,尤其是在最后,如果都是零,直接给出EOB(End of Block)即可
在JPEG编码中,游程编码的形式为:(run,level)
表示连续run个0后有值为level的系数
run最多15个,用4位表示RRRR
level,类似DC,
分成16个类别,用4位SSSS表示类别号,
类内索引
对(RRRR,SSSS)采用Huffman编码,
对类内索引采用定长编码
二、实验步骤
(1)实验用图:
SOI和EOI
(2)代码结构体分析
- 结构体struct huffman_table——存储Huffman码表
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]; //当码长>9时,交给该表处理
};
- 结构体struct component——存储当前像块中关于解码的信息
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 };
- 结构体struct jdec_private——存储图像宽高、码表等信息
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,