一.实验原理
JPEG是常见的一种图像格式,由ISO与CCITT建立并开发,是一个国际数字图像压缩标准。
根据人眼视觉特性:眼睛对亮度的敏感程度要大于对色彩的敏感程度。在图像中,为了利用人类的种视角特性,从而降低数据量,通常将RGB空间表示的彩色图像变换到YCbCr颜色空间中。由于人眼对亮度Y的敏感度大于色差CrCb,因此可以在适当程度上对CrCb进行削弱以达到压缩的目的。由于原始图像是由很多独立的像素组成的,其实人眼对于每个细微像素的分辨能力很弱,只有众多像素集合一块,才能呈现出颜色连续变化的图像,因此图像中相邻两像素点,其彩色分量在很大程度上是接近的。在一幅图像内,包含了各种频率的分量,但大多数分量都属于低频信号,只在占图像区域比例很小的图像边缘的像素才含有高频信号。因此在对图像编码的时候,在图像质量不出现可察觉损失的情况下,对包含信息量大的低频谱区分配较多比特数,对包含信息量较低的高频谱区域分配较少的比特数,就能达到数据压缩目的。
将图像的色彩空间域转换到频谱域,这就用到了DCT。其作用是将图像数据去相关化,去除图像数据内部的相关性后,以便在其后将对这些图像数据分类处理——即对不同的频路部分进行不同的量化。
量化编码是JPEG编码中产生信息损失的根源,也是图像质量下降的最主要原因。简单的说,就是将频谱领域中的每个值,除以量化表中对应的常数,且四舍五入取最接近的整数,这样会把很多高频的成分四舍五入为0。量化后左上角的值较小,右下角的值较大,这样就保持低频分量、抑制高频分量的目的。这一步在实现的时候会对Y进行细量化,对Cr、Cb采用粗量化,依次来提高压缩比。因此存在两张不同的表。
经过DCT变换后,图像中的低频分量会集中在左上角,而右下角有较多的0值,因此采用Z字形编排。JPEG
算法
使用了差分脉冲编码(DPCM)技术,对相邻图像块之间量化DC洗漱的差值进行单独编码,从而再次利用相邻特性简化数据。
为了进一步提高压缩比例,JPEG算法对DPCM编码后的直流系数与行程编码后的交流系数使用Huffman熵编码。使用huffman码表可以简单的查表进行编码。对于AC与DC所采用的码表是不同的,对于色差和亮度的霍夫曼码表也不同。
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 | vari |
- 以TXT文件输出所有的量化矩阵和所有的HUFFMAN码表:
static int parse_DQT(struct jdec_private *priv, const unsigned char *stream)
{
......
while (stream < dqt_block_end)
{
qi = *stream++;
#if C_TRACE
fprintf(c_trace, ">DQT marker\n");
fprintf(c_trace, "DQT TABLE[%d]\n", qi & 0x0F);
fflush(c_trace);
#endif
table = priv->Q_tables[qi];
build_quantization_table(table, stream);
stream += 64;
}
......
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
static void build_quantization_table(float *qtable, const unsigned char *ref_table)
{
......
for (i=0; i<8; i++) {
for (j=0; j<8; j++) {
*qtable = ref_table[*zz++] * aanscalefactor[i] * aanscalefactor[j];
#if C_TRACE
fprintf(c_trace, "%2d\t", ref_table[zigzag[i*8+j]]);
if (j == 7)
fprintf(c_trace, "\n");
fflush(c_trace);
#endif
qtable++;
}
}
......
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
static void build_huffman_table(const unsigned char *bits, const unsigned char *vals, struct huffman_table *table)
{
......
for (i=0; huffsize[i]; i++)
{
......
#if C_TRACE
fprintf(c_trace, "val=%2.2x code=%8.8x codesize=%2.2d\n", val, code, code_size);
fflush(c_trace);
#endif
......
}
......
}
#if C_TRACE
char temp[1024];
snprintf(temp, 1024, "%s_DC.yuv", output_filename);
C_DCpicture = fopen(temp, "wb");
if (C_DCpicture == NULL)
{
printf("C_DCpicture FILE OPEN ERROR");
}
snprintf(temp, 1024, "%s_AC.yuv", output_filename);
C_ACpicture = fopen(temp, "wb");
if (C_ACpicture == NULL)
{
printf("C_ACpicture FILE OPEN ERROR");
}
#endif
static void decode_MCU_1x1_3planes(struct jdec_private *priv)
{
process_Huffman_data_unit(priv, cY);
#if C_TRACE
unsigned char tmp_dc = (unsigned char)(&priv->component_infos[cY])->DCT[0]/8;
fwrite(&tmp_dc, 1, 1, C_DCpicture);
unsigned char tmp_ac = (unsigned char)(&priv->component_infos[cY])->DCT[1];
fwrite(&tmp_ac, 1, 1, C_ACpicture);
#endif
IDCT(&priv->component_infos[cY], priv->Y, 8);
process_Huffman_data_unit(priv, cCb);
IDCT(&priv->component_infos[cCb], priv->Cb, 8);
process_Huffman_data_unit(priv, cCr);
IDCT(&priv->component_infos[cCr], priv->Cr, 8);
}
实验结果: