数据压缩实验无--jpeg解码
程序员文章站
2022-07-14 21:55:42
...
JPEG是常见的一种图像格式,由ISO与CCITT建立并开发,是一个国际数字图像压缩标准。
根据人眼视觉特性:眼睛对亮度的敏感程度要大于对色彩的敏感程度。在图像中,为了利用人类的种视角特性,从而降低数据量,通常将RGB空间表示的彩色图像变换到YCbCr颜色空间中。由于人眼对亮度Y的敏感度大于色差CrCb,因此可以在适当程度上对CrCb进行削弱以达到压缩的目的。由于原始图像是由很多独立的像素组成的,其实人眼对于每个细微像素的分辨能力很弱,只有众多像素集合一块,才能呈现出颜色连续变化的图像,因此图像中相邻两像素点,其彩色分量在很大程度上是接近的。在一幅图像内,包含了各种频率的分量,但大多数分量都属于低频信号,只在占图像区域比例很小的图像边缘的像素才含有高频信号。因此在对图像编码的时候,在图像质量不出现可察觉损失的情况下,对包含信息量大的低频谱区分配较多比特数,对包含信息量较低的高频谱区域分配较少的比特数,就能达到数据压缩目的。
将图像的色彩空间域转换到频谱域,这就用到了DCT。其作用是将图像数据去相关化,去除图像数据内部的相关性后,以便在其后将对这些图像数据分类处理——即对不同的频路部分进行不同的量化。
量化编码是JPEG编码中产生信息损失的根源,也是图像质量下降的最主要原因。简单的说,就是将频谱领域中的每个值,除以量化表中对应的常数,且四舍五入取最接近的整数,这样会把很多高频的成分四舍五入为0。量化后左上角的值较小,右下角的值较大,这样就保持低频分量、抑制高频分量的目的。这一步在实现的时候会对Y进行细量化,对Cr、Cb采用粗量化,依次来提高压缩比。因此存在两张不同的表。
经过DCT变换后,图像中的低频分量会集中在左上角,而右下角有较多的0值,因此采用Z字形编排。JPEG算法使用了差分脉冲编码(DPCM)技术,对相邻图像块之间量化DC洗漱的差值进行单独编码,从而再次利用相邻特性简化数据。
JPEG图像格式介绍:
部分代码:
根据人眼视觉特性:眼睛对亮度的敏感程度要大于对色彩的敏感程度。在图像中,为了利用人类的种视角特性,从而降低数据量,通常将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 |
3个结构体
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 */ //前一个宏块DC值
short int DCT[64]; /* DCT coef */ //dct变换后的64个参数
#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 //最上层结构体,MCU块层次,包含了该MCU的信息和元素
{
/* Public variables */
uint8_t *components[COMPONENTS];//指向该MCU所包含的宏块指针
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;//若不包含Huffman码表时所用的缺省码表
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];//三个彩色分量最基本MCU块大小 8x8x4、8x8、8x8
jmp_buf jump_state;
/* Internal Pointer use for colorspace conversion, do not modify it !!! */
uint8_t *plane[COMPONENTS];
};
输出yuv图像:
量化矩阵:
输出直流交流图像:
int tinyjpeg_decode(struct jdec_private *priv, int pixfmt)
{
/*add by dyq*/
priv->DC_Image = (short int *)malloc(sizeof(short int)*priv->height*priv->width / 64);
priv->AC_Image = (short int *)malloc(sizeof(short int)*priv->height*priv->width / 64);
/* end */
unsigned int x, y, xstride_by_mcu, ystride_by_mcu;
unsigned int bytes_per_blocklines[3], bytes_per_mcu[3];
/*
typedef void (*decode_MCU_fct) (struct jdec_private *priv);
typedef void (*convert_colorspace_fct) (struct jdec_private *priv);
这两个函数里面结构体作为函数参数实现面向过程程序设计中又分层次的思想。
*/
decode_MCU_fct decode_MCU;
const decode_MCU_fct *decode_mcu_table;
const convert_colorspace_fct *colorspace_array_conv;
convert_colorspace_fct convert_to_pixfmt;
/*...省略部分代码*/
decode_mcu_table = decode_mcu_3comp_table;
return -1;
}
//选择MCU的长宽
xstride_by_mcu = ystride_by_mcu = 8;
if ((priv->component_infos[cY].Hfactor | priv->component_infos[cY].Vfactor) == 1)
{
decode_MCU = decode_mcu_table[0];
convert_to_pixfmt = colorspace_array_conv[0];
} else if (priv->component_infos[cY].Hfactor == 1) {
decode_MCU = decode_mcu_table[1];
convert_to_pixfmt = colorspace_array_conv[1];
ystride_by_mcu = 16;
} else if (priv->component_infos[cY].Vfactor == 2) {
decode_MCU = decode_mcu_table[3];
convert_to_pixfmt = colorspace_array_conv[3];
xstride_by_mcu = 16;
ystride_by_mcu = 16;
} else {
decode_MCU = decode_mcu_table[2];
convert_to_pixfmt = colorspace_array_conv[2];
xstride_by_mcu = 16;
}
resync(priv);
/*...省略部分代码*/
/* Just the decode the image by macroblock (size is 8x8, 8x16, or 16x16) */
for (y=0; y < priv->height/ystride_by_mcu; y++)
{
//trace("Decoding row %d\n", y);
priv->plane[0] = priv->components[0] + (y * bytes_per_blocklines[0]);
priv->plane[1] = priv->components[1] + (y * bytes_per_blocklines[1]);
priv->plane[2] = priv->components[2] + (y * bytes_per_blocklines[2]);
for (x=0; x < priv->width; x+=xstride_by_mcu)
{
//在此处取直流分量和交流分量进行输出
getDAC(priv, xstride_by_mcu, ystride_by_mcu);//add by dyq
decode_MCU(priv);//huffman 解码
convert_to_pixfmt(priv);//变换
priv->plane[0] += bytes_per_mcu[0];
priv->plane[1] += bytes_per_mcu[1];
priv->plane[2] += bytes_per_mcu[2];
if (priv->restarts_to_go>0)
{
priv->restarts_to_go--;
if (priv->restarts_to_go == 0)
{
priv->stream -= (priv->nbits_in_reservoir/8);
resync(priv);
if (find_next_rst_marker(priv) < 0)
return -1;
}
}
}
}
/* add by dyq */
//全部取出后作范围调整[0,255]
adjustDAC(priv, xstride_by_mcu, ystride_by_mcu);
/* end */
return 0;
}
static void getDAC(struct jdec_private *priv, int xstride_by_mcu , int ystride_by_mcu)
{
static long int i = 0;
//因为宏块为8*8 因此DC图像应该为实际图像大小除以宏块大小
if (i < priv->height*priv->width / (xstride_by_mcu * ystride_by_mcu) )
{
priv->DC_Image[i] = priv->component_infos[0].DCT[0];
priv->AC_Image[i] = priv->component_infos[0].DCT[1];
}
i++;
}
static void adjustDAC(struct jdec_private *priv, unsigned int xstride_by_mcu, unsigned int ystride_by_mcu)
{
int i;
short int min_AC, max_AC, min_DC, max_DC, size;
unsigned char *temp_AC, *temp_DC;
size = priv->height*priv->width / (xstride_by_mcu * ystride_by_mcu);
temp_AC = (unsigned char*)malloc(sizeof(unsigned char)*size);
temp_DC = (unsigned char*)malloc(sizeof(unsigned char)*size);
//将AC/DC分量范围调整到[0-255]
min_AC = priv->AC_Image[0];
max_AC = priv->AC_Image[0];
min_DC = priv->DC_Image[0];
max_DC = priv->DC_Image[0];
//遍历寻找最大最小值,进行中心化操作
for (i = 0; i < size; i++)
{
if (priv->AC_Image[i] < min_AC)
min_AC = priv->AC_Image[i];
if (priv->AC_Image[i] > max_AC)
max_AC = priv->AC_Image[i];
if (priv->DC_Image[i] < min_DC)
min_DC = priv->DC_Image[i];
if (priv->DC_Image[i] > max_DC)
max_DC = priv->DC_Image[i];
}
//范围调整
for (i = 0; i < size; i++)
{
temp_AC[i] = (unsigned char)(255 * (priv->AC_Image[i] - min_AC) / (max_AC - min_AC));
temp_DC[i] = (unsigned char)(255 * (priv->DC_Image[i] - min_DC) / (max_DC - min_DC));
}
fwrite(temp_AC, 1, size, AC_FILE);
fwrite(temp_DC, 1, size, DC_FILE);
free(temp_AC);
free(temp_DC);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
得到结果如下:
上一篇: 阿里云ossutil