JPEG的原理分析
JPEG的编码
JPEG的编码流程图
1.零电平偏置:将所有像素的数值-128,范围从[0,255]变为[-128,127],便于DCT变换量化后数据量减少;
2.8×8DCT变换,将图像分为8×8的块并作为二维DCT的输入,输出结果低频集中在左上角且数值较大。实现能量集中和去相关,便于去除图像的空间冗余;
3.量化:量化步距按照系数所在的位置和颜色分量来确定,使用基于人眼视觉特性的量化表进行量化,低频细量化高频粗量化,减少视觉冗余;
4.直流分量DC系数进行差分编码和VLC编码,交流分量AC系数进行之字扫后进行游程编码再进行VLC编码。
DC 系数编码
由于直流系数 F(0,0)反映了该子图像中包含的直流成分,通常较大,又由于两个相邻的子图像的直流系数通常具有较大的相关性,所以对 DC 系数采用差值脉冲编码(DPCM),即对本像素块直流系数与前一像素块直流系数的差值进行无损编码。
插值DIFF(k)=DC(k)-DC(k-1)
对DIFF进行编码:
对DIFF用Huffman编码:分成类别,类似指数Golomb编码
➢ 类别ID:一元码编码
➢ 类内索引:采用定长码
例:DC=8,上一DC=5,则DIFF=8-5=3
类别ID=2,类内索引=3,则码流=10011
AC 系数编码
首先,进行游程编码(RLC),并在最后加上块结束码(EOB);然后,系数序列分组,将非零系数和它前面的相邻的全部零系数分在一组内;每组用两个符号表示[(Run,Size),(Amplitude)]
Amplitude:表示非零系数的幅度值;
Run:表示零的游程即零的个数,最多15个,用4位表示RRRR;
Size:表示非零系数的幅度值的编码位数;
◼ 分成16个类别,用4位表示SSSS表示类别号
◼ 类内索引
➢ 对(RRRR, SSSS)联合用Huffman编码
➢ 对类内索引用定长码编码
例:AC系数为0,2,0,0,3,0,-4,0,0,0,-6,0,0,5,7……
表示为(1, 2), (2, 3) ,(1,4),(3,6),(2,5)……
JPEG文件格式
Segment 的组织形式
JPEG 在文件中以 Segment 的形式组织,它具有以下特点:
1.均以 0xFF 开始,后跟 1 byte 的 Marker 和 2 byte 的 Segment length(包含表示Length 本身所占用的 2byte,不含“0xFF” + “Marker” 所占用的 2 byte);
2. 采用 Motorola 序(相对于 Intel 序),即保存时高位在前,低位在后;
3. Data 部分中,0xFF 后若为 0x00,则跳过此字节不予处理;
JPEG 的 的 Segment Marker
test文件:
FFD8: SOI,Start of Image,图像开始的所有的 JPEG 文件必须以 SOI 开始;
FFE0: APP0,Application,应用程序保留标记 0;
length(2 byte): 16 byte (0010)
标识符(5 byte): JFIF(4A 46 49 46 00)
版本(2 byte): 0101,JFIF的版本号1.1
Units(1 byte):00,X和Y的密度单位,0:无单位;1:点数/英寸;2:点数/厘米
水平方向点密度(2 bytes):0001
垂直方向点密度(2 bytes):0001
缩略图水平像素数目: 00 (1 byte)
缩略图垂直像素数目: 00 (1 byte)
FFDB: DQT,Define Quantization Table,定义量化表
00 43 00 02 01 01 01 01 01 02 01 01 01 02 02 02 02 02 04 03 02 02 02 02 05 04 04 03 04 06 05 06 06 06 05 06 06 06 07 09 08 06 07 09 07 06 06 08 0B 08 09 0A 0A 0A 0A 0A 06 08 0B 0C 0B 0A 0C 09 0A 0A 0A
亮度量化表数据长度(2 bytes): 67 byte (0043)
精度:(00)高四位,0:8位;1:16位;
量化表ID:(00)低四位,取值范围0~3;
量化表:
i: 00 value: 2
i: 01 value: 1
i: 02 value: 1
i: 03 value: 1
i: 04 value: 1
i: 05 value: 1
i: 06 value: 2
i: 07 value: 1
i: 08 value: 1
……
i: 62 value: 10
i: 63 value: 10
FFDB: DQT,Define Quantization Table,定义量化表
00 43 01 02 02 02 02 02 02 05 03 03 05 0A 07 06 07 0A 0A 0A 0A 0A 0A 0A 0A 0A 0A 0A 0A 0A 0A 0A 0A 0A 0A 0A 0A 0A 0A 0A 0A 0A 0A 0A 0A 0A 0A 0A 0A 0A 0A 0A 0A 0A 0A 0A 0A 0A 0A 0A 0A 0A 0A 0A 0A 0A 0A
色度量化表数据长度(2 bytes): 67 byte (0043)
精度:(01)高四位,0:8位;1:16位;
量化表ID:(01)低四位,取值范围0~3;
量化表:(略)
FFC0:start of frame,帧图像开始
00 11 08 04 00 04 00 03 01 11 00 02 11 01 03 11 01
帧图像长度:0011(17 bytes)
图像精度(每个数据样本的位数): 8
Image Height: 1024 (2 byte)
Image Width: 1024 (2 byte)
颜色分量数: 03(YCrCb) (1 byte)
颜色分量 ID: 01 (1 byte) (Y)
SampRate_Y_H: 01 (Higher 4 bit)
SampRate_Y_V: 01 (Lower 4 bit)
Y QtTableID: 00 (1 byte)
颜色分量 ID: 02 (1 byte) (U)
SampRate_U_H: 01 (Higher 4 bit)
SampRate_U_V: 01 (Lower 4 bit)
U QtTableID: 01 (1 byte)
颜色分量 ID: 03 (1 byte) (V)
SampRate_V_H: 01 (Higher 4 bit)
SampRate_V_V: 01 (Lower 4 bit)
V QtTableID: 01 (1 byte)
颜色分量信息:每个分量有三个字节,第一个为分量的 ID ,01 :Y ,02 :U, 03:V ;第二个字节高位为水平采样因子,低位为垂直采样因子,这里三个分量的采样率相同,所以采样格式为 4 :4 :4 ;第三个字节代表这个分量对应的量化表 表 ID 可以看出,Y 对应的量化表 ID 索引值为 00,而 UV 对应的量化表 ID都为 01,即它们共用一张量化表。
FFC4: DHT,Define Huffman Table,定义 Huffman 树表
00 1D 00 00 03 01 01 01 01 01 01 01 00 00 00 00 00 00 00 04 05 06 03 02 01 00 09 07 08
length(2 byte): 29byte (001D)
Huffman 表类型: 0 (Higher 4 bit) (DC)
Huffman 表 ID: 0 (Lower 4 bit) (0 号表)
不同长度Huffman的码字数量: (16 byte)
CodeLength: 01 00 个
CodeLength: 02 03 个
……
CodeLength: 16 00 个
编码内容:04 05 06 03 02 01 00 09 07 08
这 16 个字节之后的3+1×7=10 个字节对应的就是每个符字对应的权值,这些权值的含义即为 DC 系数经 DPCM 编码后幅度值的位长。根据解码得到的位长来读取之后相应长度的码字,再查上面这张可变长二进制编码表,就可以得到直流系数的幅度值,注意这个幅度值是经过 DPCM 差 分编码得到的。
FFC4: DHT,Define Huffman Table,定义 Huffman 树表
00 3E 10 00 01 02 05 03 03 03 02 05 03 04 02 02 02 01 05 00 01 03 02 04 05 11 21 22 31 61 06 12 A1 32 41 62 13 51 23 42 71 81 91 15 52 63 07 14 33 53 16 43 08 B1 34 C1 24 D1 09 72 F0 A2
length(2 byte): 62 byte (003E)
Huffman 表类型: 1 (Higher 4 bit) (AC)
Huffman 表 ID: 0 (Lower 4 bit) (0 号表)
Huffman 表(略)
交流系数表(长度也为16bytes):它和直流系数表的权值代表的含义与解码方式有一定的差别,交流系数权值的高位代表游程 run 的值,低位与直流系数相同,代表幅度的位长 size. 在得到 位长后还是要查可变长二制编码表来得到真正的 AC 幅值。
FFC4: DHT,Define Huffman Table,定义 Huffman 树表
00 1E 01 00 03 01 01 01 01 01 01 01 01 00 00 00 00 00 00 04 05 06 03 02 01 00 07 0A 09 08
length(2 byte): 30 byte (001E)
Huffman 表类型: 0 (Higher 4 bit) (DC)
Huffman 表 ID: 1 (Lower 4 bit) (1 号表)
Huffman 表(略)
FFC4: DHT,Define Huffman Table,定义 Huffman 树表
00 2F 11 00 02 03 00 02 02 02 03 01 00 03 00 03 00 00 07 00 04 01 02 03 31 61 11 12 05 21 13 14 41 51 06 22 32 07 15 42 08 71 23 24 33 81 A1
length(2 byte): 47 byte (002F)
Huffman 表类型: 1 (Higher 4 bit) (AC)
Huffman 表 ID: 1 (Lower 4 bit) (1 号表)
Huffman 表(略)
FFDA: SOS,Start of Scan,扫描开始
00 0C 03 01 00 02 11 03 11 00 3F 00
length(2 byte): 12 byte (000C)
颜色分量数(1 byte):应该和 SOF 中的颜色分量数相同:03
颜色分量 ID: 1 (1 byte) (Y)
Y Dc HuffmanTreeIndex: 0 (Higher 4 bit)
Y Ac HuffmanTreeIndex: 0 (Lower 4 bit)
颜色分量 ID: 2 (1 byte) (U or V)
UV Dc HuffmanTreeIndex: 1 (Higher 4 bit)
UV Ac HuffmanTreeIndex: 1 (Lower 4 bit)
颜色分量 ID: 3 (1 byte) (U or V)
UV Dc HuffmanTreeIndex: 1 (Higher 4 bit)
UV Ac HuffmanTreeIndex: 1 (Lower 4 bit)
熵编码数据
FFD9: End of Image,图像结束
JPEG的解码
一 读取文件
/**
* Load one jpeg image, and decompress it, and save the result.
*/
int convert_one_image(const char *infilename, const char *outfilename, int output_format)
{
FILE *fp;
unsigned int length_of_file; //文件长度
unsigned int width, height; //图像宽、高
unsigned char *buf; //缓冲区
struct jdec_private *jdec; //引用结构体
unsigned char *components[3];
/* Load the Jpeg into memory */
fp = fopen(infilename, "rb"); //打开图像文件
if (fp == NULL)
exitmessage("Cannot open filename\n");
length_of_file = filesize(fp); //读取文件大小
buf = (unsigned char *)malloc(length_of_file + 4);//开辟缓冲区
if (buf == NULL)
exitmessage("Not enough memory for loading file\n");
fread(buf, length_of_file, 1, fp);//将图像文件数据读入缓冲区中
fclose(fp);
/* Decompress it */
jdec = tinyjpeg_init();//初始化一个指针
if (jdec == NULL)
exitmessage("Not enough memory to alloc the structure need for decompressing\n");
if (tinyjpeg_parse_header(jdec, buf, length_of_file)<0)//解析jpeg文件头
exitmessage(tinyjpeg_get_errorstring(jdec));
/* Get the size of the image */
tinyjpeg_get_size(jdec, &width, &height);//得到文件宽高
snprintf(error_string, sizeof(error_string),"Decoding JPEG image...\n");
if (tinyjpeg_decode(jdec, output_format) < 0)//根据想要解析出的格式进行解析准备(开辟空间)
exitmessage(tinyjpeg_get_errorstring(jdec));
/*
* Get address for each plane (not only max 3 planes is supported), and
* depending of the output mode, only some components will be filled
* RGB: 1 plane, YUV420P: 3 planes, GREY: 1 plane
*/
tinyjpeg_get_components(jdec, components);//将文件YUV数据赋值给数组components
/* Save it */
switch (output_format)//写出解析文件
{
case TINYJPEG_FMT_RGB24:
case TINYJPEG_FMT_BGR24:
write_tga(outfilename, output_format, width, height, components);
break;
case TINYJPEG_FMT_YUV420P:
write_yuv(outfilename, width, height, components);
break;
case TINYJPEG_FMT_GREY:
write_pgm(outfilename, width, height, components);
break;
}
/* Only called this if the buffers were allocated by tinyjpeg_decode() */
tinyjpeg_free(jdec);
/* else called just free(jdec); */
free(buf);
return 0;
}
二 解析 Segment Marker
1解析 SOI
/**
* Initialize the tinyjpeg object and prepare the decoding of the stream.
*
* Check if the jpeg can be decoded with this jpeg decoder.
* Fill some table used for preprocessing.
*/
int tinyjpeg_parse_header(struct jdec_private *priv, const unsigned char *buf, unsigned int size)
{
int ret;
/* Identify the file */
if ((buf[0] != 0xFF) || (buf[1] != SOI))//解析文件头,是为JPEG文件
snprintf(error_string, sizeof(error_string),"Not a JPG file ?\n");
//若是JPEG文件
priv->stream_begin = buf+2;//跳过标识符
priv->stream_length = size-2;//文件长度相应-2bytes
priv->stream_end = priv->stream_begin + priv->stream_length;//确定文件结束标识符位置
ret = parse_JFIF(priv, priv->stream_begin);//开始解析
return ret;
}
2 解析 APP0
检查标识“JFIF”及版本
得到一些参数
(解析标识符)
static int parse_JFIF(struct jdec_private *priv, const unsigned char *stream)
{
int chuck_len;
int marker;
int sos_marker_found = 0;
int dht_marker_found = 0;
const unsigned char *next_chunck;
/* Parse marker */
while (!sos_marker_found)
{
if (*stream++ != 0xff)
goto bogus_jpeg_format;
/* Skip any padding ff byte (this is normal) */
while (*stream == 0xff)
stream++;
marker = *stream++;
chuck_len = be16_to_cpu(stream);//得到文件头长度
next_chunck = stream + chuck_len;//下一个标识符位置
switch (marker)//判断该标识符属于什么标识符并写入track_jpeg
{
case SOF:
if (parse_SOF(priv, stream) < 0)
return -1;
break;
case DQT:
if (parse_DQT(priv, stream) < 0)
return -1;
break;
case SOS:
if (parse_SOS(priv, stream) < 0)
return -1;
sos_marker_found = 1;
break;
case DHT:
if (parse_DHT(priv, stream) < 0)
return -1;
dht_marker_found = 1;
break;
case DRI:
if (parse_DRI(priv, stream) < 0)
return -1;
break;
default:
#if TRACE
fprintf(p_trace,"> Unknown marker %2.2x\n", marker);
fflush(p_trace);
#endif
break;
}
stream = next_chunck;//指针指向下一个标识符
}
if (!dht_marker_found) {
#if TRACE
fprintf(p_trace,"No Huffman table loaded, using the default one\n");
fflush(p_trace);
#endif
build_default_huffman_tables(priv);
}
#ifdef SANITY_CHECK
if ( (priv->component_infos[cY].Hfactor < priv->component_infos[cCb].Hfactor)
|| (priv->component_infos[cY].Hfactor < priv->component_infos[cCr].Hfactor))
snprintf(error_string, sizeof(error_string),"Horizontal sampling factor for Y should be greater than horitontal sampling factor for Cb or Cr\n");
if ( (priv->component_infos[cY].Vfactor < priv->component_infos[cCb].Vfactor)
|| (priv->component_infos[cY].Vfactor < priv->component_infos[cCr].Vfactor))
snprintf(error_string, sizeof(error_string),"Vertical sampling factor for Y should be greater than vertical sampling factor for Cb or Cr\n");
if ( (priv->component_infos[cCb].Hfactor!=1)
|| (priv->component_infos[cCr].Hfactor!=1)
|| (priv->component_infos[cCb].Vfactor!=1)
|| (priv->component_infos[cCr].Vfactor!=1))
snprintf(error_string, sizeof(error_string),"Sampling other than 1x1 for Cr and Cb is not supported");
#endif
return 0;
bogus_jpeg_format:
#if TRACE
fprintf(p_trace,"Bogus jpeg format\n");
fflush(p_trace);
#endif
return -1;
}
3 解析 DQT
得到量化表长度(可能包含多张量化表)
得到量化表的精度
得到及检查量化表的序号(只能是 0 —— 3)
得到量化表内容(64 个数据)
static int parse_DQT(struct jdec_private *priv, const unsigned char *stream)
{
int qi;
float *table;
const unsigned char *dqt_block_end;
#if TRACE
fprintf(p_trace,"> DQT marker\n");
fflush(p_trace);
#endif
dqt_block_end = stream + be16_to_cpu(stream);//确定量化表结束位置
stream += 2; /* Skip length */
while (stream < dqt_block_end)
{
qi = *stream++;
#if SANITY_CHECK
if (qi>>4)
snprintf(error_string, sizeof(error_string),"16 bits quantization table is not supported\n");
if (qi>4)
snprintf(error_string, sizeof(error_string),"No more 4 quantization table is supported (got %d)\n", qi);
#endif
table = priv->Q_tables[qi];
build_quantization_table(table, stream);//建立量化表
stream += 64;//量化表长度固定为64bytes
}
#if TRACE
fprintf(p_trace,"< DQT marker\n");
fflush(p_trace);
#endif
return 0;
}
得到量化表:
static void build_quantization_table(float *qtable, const unsigned char *ref_table)
{
int i, j;
static const double aanscalefactor[8] = {
1.0, 1.387039845, 1.306562965, 1.175875602,
1.0, 0.785694958, 0.541196100, 0.275899379
};
const unsigned char *zz = zigzag;
for (i=0; i<8; i++) {
for (j=0; j<8; j++) {
*qtable++ = ref_table[*zz++] * aanscalefactor[i] * aanscalefactor[j];
}
}
}
4 解析 SOF
得到每个 sample 的比特数、长宽、颜色分量数
得到每个颜色分量的 ID、水平采样因子、垂直采样因子、使用的量化表
序号(与 DQT 中序号对应)
static int parse_SOF(struct jdec_private *priv, const unsigned char *stream)
{
int i, width, height, nr_components, cid, sampling_factor;
int Q_table;
struct component *c;
#if TRACE
fprintf(p_trace,"> SOF marker\n");
fflush(p_trace);
#endif
print_SOF(stream);
height = be16_to_cpu(stream+3);//帧图像高
width = be16_to_cpu(stream+5);//帧图像宽
nr_components = stream[7];//颜色分量数
#if SANITY_CHECK
if (stream[2] != 8)
snprintf(error_string, sizeof(error_string),"Precision other than 8 is not supported\n");
if (width>JPEG_MAX_WIDTH || height>JPEG_MAX_HEIGHT)
snprintf(error_string, sizeof(error_string),"Width and Height (%dx%d) seems suspicious\n", width, height);
if (nr_components != 3)
snprintf(error_string, sizeof(error_string),"We only support YUV images\n");
if (height%16)
snprintf(error_string, sizeof(error_string),"Height need to be a multiple of 16 (current height is %d)\n", height);
if (width%16)
snprintf(error_string, sizeof(error_string),"Width need to be a multiple of 16 (current Width is %d)\n", width);
#endif
stream += 8;//指向颜色分量ID
for (i=0; i<nr_components; i++) {
cid = *stream++;//颜色分量ID
sampling_factor = *stream++;//水平、垂直采样因子
Q_table = *stream++;//对应量化表ID
c = &priv->component_infos[i];
#if SANITY_CHECK
c->cid = cid;
if (Q_table >= COMPONENTS)
snprintf(error_string, sizeof(error_string),"Bad Quantization table index (got %d, max allowed %d)\n", Q_table, COMPONENTS-1);
#endif
c->Vfactor = sampling_factor&0xf;//垂直采样因子
c->Hfactor = sampling_factor>>4;//水平采样因子
c->Q_table = priv->Q_tables[Q_table];
#if TRACE
fprintf(p_trace,"Component:%d factor:%dx%d Quantization table:%d\n",
cid, c->Hfactor, c->Hfactor, Q_table );
fflush(p_trace);
#endif
}
priv->width = width;
priv->height = height;
#if TRACE
fprintf(p_trace,"< SOF marker\n");
fflush(p_trace);
#endif
return 0;
}
5 解析 DHT
得到 Huffman 表的类型(AC、DC)、序号
依据数据重建 Huffman 表
static int parse_DHT(struct jdec_private *priv, const unsigned char *stream)
{
unsigned int count, i;
unsigned char huff_bits[17];
int length, index;
length = be16_to_cpu(stream) - 2;//huffman表长度
stream += 2; /* Skip length */
#if TRACE
fprintf(p_trace,"> DHT marker (length=%d)\n", length);
fflush(p_trace);
#endif
while (length>0) {
index = *stream++;//确定huffman表类型
/* We need to calculate the number of bytes 'vals' will takes */
huff_bits[0] = 0;
count = 0;
for (i=1; i<17; i++) {//不同长度Huffman的码字数量
huff_bits[i] = *stream++;
count += huff_bits[i];//16个字节后对应码字数量之和的字节对应的就是每个符字对应的权值
}
#if SANITY_CHECK
if (count >= HUFFMAN_BITS_SIZE)
snprintf(error_string, sizeof(error_string),"No more than %d bytes is allowed to describe a huffman table", HUFFMAN_BITS_SIZE);
if ( (index &0xf) >= HUFFMAN_TABLES)
snprintf(error_string, sizeof(error_string),"No more than %d Huffman tables is supported (got %d)\n", HUFFMAN_TABLES, index&0xf);
#if TRACE
fprintf(p_trace,"Huffman table %s[%d] length=%d\n", (index&0xf0)?"AC":"DC", index&0xf, count);
fflush(p_trace);
#endif
#endif
if (index & 0xf0 )
build_huffman_table(huff_bits, stream, &priv->HTAC[index&0xf]);//高四位为1是AC表
else
build_huffman_table(huff_bits, stream, &priv->HTDC[index&0xf]);//高四位为0是DC表
length -= 1;
length -= 16;
length -= count;
stream += count;//指向下一标识符
}
#if TRACE
fprintf(p_trace,"< DHT marker\n");
fflush(p_trace);
#endif
return 0;
}
static int parse_DRI(struct jdec_private *priv, const unsigned char *stream)
{
unsigned int length;
#if TRACE
fprintf(p_trace,"> DRI marker\n");
fflush(p_trace);
#endif
length = be16_to_cpu(stream);
#if SANITY_CHECK
if (length != 4)
snprintf(error_string, sizeof(error_string),"Length of DRI marker need to be 4\n");
#endif
priv->restart_interval = be16_to_cpu(stream+2);
#if TRACE
fprintf(p_trace,"Restart interval = %d\n", priv->restart_interval);
fprintf(p_trace,"< DRI marker\n");
fflush(p_trace);
#endif
return 0;
}
建立huffman表:
static void build_huffman_table(const unsigned char *bits, const unsigned char *vals, struct huffman_table *table)
{
unsigned int i, j, code, code_size, val, nbits;
unsigned char huffsize[HUFFMAN_BITS_SIZE+1], *hz;
unsigned int huffcode[HUFFMAN_BITS_SIZE+1], *hc;
int next_free_entry;
/*
* Build a temp array
* huffsize[X] => numbers of bits to write vals[X]
*/
hz = huffsize;
for (i=1; i<=16; i++)
{
for (j=1; j<=bits[i]; j++)//bits[i]表示i编码长度的出现次数
*hz++ = i;
}
*hz = 0;
memset(table->lookup, 0xff, sizeof(table->lookup));//初始化lookup表内容
for (i=0; i<(16-HUFFMAN_HASH_NBITS); i++)
table->slowtable[i][0] = 0;
/* Build a temp array
* huffcode[X] => code used to write vals[X]
*/
code = 0;
hc = huffcode;
hz = huffsize;
nbits = *hz;
while (*hz)
{
while (*hz == nbits)//当当前编码长度没有改变时
{
*hc++ = code++;//编码直接加1
hz++;
}
code <<= 1;//当长度变化(+1)时,编码左移1位
nbits++;
}
/*
* Build the lookup table, and the slowtable if needed.
*/
next_free_entry = -1;
for (i=0; huffsize[i]; i++)
{
val = vals[i];//权值
code = huffcode[i];//编码
code_size = huffsize[i];//码长
#if TRACE
fprintf(p_trace,"val=%2.2x code=%8.8x codesize=%2.2d\n", val, code, code_size);
fflush(p_trace);
#endif
table->code_size[val] = code_size;//此时码长为对应权值的码长,得到code查找表
if (code_size <= HUFFMAN_HASH_NBITS)//若码长小于查找表固定长度
{
/*
* Good: val can be put in the lookup table, so fill all value of this
* column with value val
*/
int repeat = 1UL<<(HUFFMAN_HASH_NBITS - code_size);
code <<= HUFFMAN_HASH_NBITS - code_size;//编码位数补全
while ( repeat-- )
table->lookup[code++] = val;//建立查找表和权值的关系,得到value查找表
}
else
{
/* Perhaps sorting the array will be an optimization */
uint16_t *slowtable = table->slowtable[code_size-HUFFMAN_HASH_NBITS-1];//当码字长度大于9,slowtable处理
while(slowtable[0])
slowtable+=2;
slowtable[0] = code;
slowtable[1] = val;
slowtable[2] = 0;
/* TODO: NEED TO CHECK FOR AN OVERFLOW OF THE TABLE */
}
}
}
得到下列类似查找表:
6 解析 SOS
得到解析每个颜色分量的 DC、AC 值所使用的 Huffman 表序号(与 DHT
中序号对应)
static int parse_SOS(struct jdec_private *priv, const unsigned char *stream)
{
unsigned int i, cid, table;
unsigned int nr_components = stream[2];//颜色分量数
#if TRACE
fprintf(p_trace,"> SOS marker\n");
fflush(p_trace);
#endif
#if SANITY_CHECK
if (nr_components != 3)
snprintf(error_string, sizeof(error_string),"We only support YCbCr image\n");
#endif
stream += 3;//跳过标识符和长度
for (i=0;i<nr_components;i++) {
cid = *stream++;//颜色分量ID
table = *stream++;//高四位:Dc HuffmanTreeIndex;低四位:Ac HuffmanTreeIndex
#if SANITY_CHECK
if ((table&0xf)>=4)
snprintf(error_string, sizeof(error_string),"We do not support more than 2 AC Huffman table\n");
if ((table>>4)>=4)
snprintf(error_string, sizeof(error_string),"We do not support more than 2 DC Huffman table\n");
if (cid != priv->component_infos[i].cid)
snprintf(error_string, sizeof(error_string),"SOS cid order (%d:%d) isn't compatible with the SOF marker (%d:%d)\n",
i, cid, i, priv->component_infos[i].cid);
#if TRACE
fprintf(p_trace,"ComponentId:%d tableAC:%d tableDC:%d\n", cid, table&0xf, table>>4);
fflush(p_trace);
#endif
#endif
priv->component_infos[i].AC_table = &priv->HTAC[table&0xf];
priv->component_infos[i].DC_table = &priv->HTDC[table>>4];
}
priv->stream = stream+3;//结束SOS
#if TRACE
fprintf(p_trace,"< SOS marker\n");
fflush(p_trace);
#endif
return 0;
}
三 依据每个分量的水平、垂直采样因子计算 MCU 的大小个 ,并得到每个 MCU 中8×8宏块的个数
xstride_by_mcu = ystride_by_mcu = 8;//初始化为4:4:4
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];
#if TRACE
fprintf(p_trace,"Use decode 1x1 sampling\n");
fflush(p_trace);
#endif
} 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;
#if TRACE
fprintf(p_trace,"Use decode 1x2 sampling (not supported)\n");
fflush(p_trace);
#endif
} 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;
#if TRACE
fprintf(p_trace,"Use decode 2x2 sampling\n");
fflush(p_trace);
#endif
} else {
decode_MCU = decode_mcu_table[2];
convert_to_pixfmt = colorspace_array_conv[2];
xstride_by_mcu = 16;
#if TRACE
fprintf(p_trace,"Use decode 2x1 sampling\n");
fflush(p_trace);
#endif
}
四 对每个 MCU 解码(依照各分量水平、垂直采样因子对 MCU 中每个 分量 宏块解码)
1 对每个宏块进行 Huffman 解码,得到 DCT 系数
static void decode_MCU_1x1_3planes(struct jdec_private *priv)
{
// Y
process_Huffman_data_unit(priv, cY);
IDCT(&priv->component_infos[cY], priv->Y, 8);
// Cb
process_Huffman_data_unit(priv, cCb);
IDCT(&priv->component_infos[cCb], priv->Cb, 8);
// Cr
process_Huffman_data_unit(priv, cCr);
IDCT(&priv->component_infos[cCr], priv->Cr, 8);
}
/*
* Decode a 2x1
* .-------.
* | 1 | 2 |
* `-------'
*/
static void decode_MCU_2x1_3planes(struct jdec_private *priv)//4:2:2
{
// Y
process_Huffman_data_unit(priv, cY);
IDCT(&priv->component_infos[cY], priv->Y, 16);
process_Huffman_data_unit(priv, cY);
IDCT(&priv->component_infos[cY], priv->Y+8, 16);
// Cb
process_Huffman_data_unit(priv, cCb);
IDCT(&priv->component_infos[cCb], priv->Cb, 8);
// Cr
process_Huffman_data_unit(priv, cCr);
IDCT(&priv->component_infos[cCr], priv->Cr, 8);
}
/*
* Decode a 2x2
* .-------.
* | 1 | 2 |
* |---+---|
* | 3 | 4 |
* `-------'
*/
static void decode_MCU_2x2_3planes(struct jdec_private *priv)//4:2:0
{
// Y
process_Huffman_data_unit(priv, cY);
IDCT(&priv->component_infos[cY], priv->Y, 16);
process_Huffman_data_unit(priv, cY);
IDCT(&priv->component_infos[cY], priv->Y+8, 16);
process_Huffman_data_unit(priv, cY);
IDCT(&priv->component_infos[cY], priv->Y+64*2, 16);
process_Huffman_data_unit(priv, cY);
IDCT(&priv->component_infos[cY], priv->Y+64*2+8, 16);
// Cb
process_Huffman_data_unit(priv, cCb);
IDCT(&priv->component_infos[cCb], priv->Cb, 8);
// Cr
process_Huffman_data_unit(priv, cCr);
IDCT(&priv->component_infos[cCr], priv->Cr, 8);
}
static void process_Huffman_data_unit(struct jdec_private *priv, int component)
{
unsigned char j;
unsigned int huff_code;
unsigned char size_val, count_0;
struct component *c = &priv->component_infos[component];//获取分量信息
short int DCT[64];
/* Initialize the DCT coef table */
memset(DCT, 0, sizeof(DCT));
/* DC coefficient decoding */
huff_code = get_next_huffman_code(priv, c->DC_table);
//trace("+ %x\n", huff_code);
if (huff_code) {
//DC系数采用差分编码,所以真值=当前值-前一个值
get_nbits(priv->reservoir, priv->nbits_in_reservoir, priv->stream, huff_code, DCT[0]);
DCT[0] += c->previous_DC;// DC 系数采用差分编码, 恢复原值
c->previous_DC = DCT[0];
} else {
DCT[0] = c->previous_DC;
}
/* AC coefficient decoding */
j = 1;
while (j<64)
{
huff_code = get_next_huffman_code(priv, c->AC_table);
//trace("- %x\n", huff_code);
size_val = huff_code & 0xF;// Amplitude 幅度
count_0 = huff_code >> 4;// 零游程长度
if (size_val == 0)// 0 不是一个有效的 Amplitude 值,这里做零游程标志
{ /* RLE */
if (count_0 == 0)
break; /* EOB found, go out */
else if (count_0 == 0xF)
j += 16; /* skip 16 zeros */
}
else
{
j += count_0; /* skip count_0 zeroes */
if (__unlikely(j >= 64))
{
snprintf(error_string, sizeof(error_string), "Bad huffman data (buffer overflow)");
break;
}
get_nbits(priv->reservoir, priv->nbits_in_reservoir, priv->stream, size_val, DCT[j]);
// 查表得到 AC DCT 系数
j++;
}
}
for (j = 0; j < 64; j++)
c->DCT[j] = DCT[zigzag[j]];// 以 以 zig-zag 序保存
}
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
};
static int get_next_huffman_code(struct jdec_private *priv, struct huffman_table *huffman_table)
{
int value, hcode;
unsigned int extra_nbits, nbits;
uint16_t *slowtable;
//读取HUFFMAN_HASH_NBITS比特,并赋值给hcode
look_nbits(priv->reservoir, priv->nbits_in_reservoir, priv->stream, HUFFMAN_HASH_NBITS, hcode);
value = huffman_table->lookup[hcode];//找到对应权值
if (__likely(value >= 0))
{
unsigned int code_size = huffman_table->code_size[value];
skip_nbits(priv->reservoir, priv->nbits_in_reservoir, priv->stream, code_size);//跳过 code_size比特
return value;
}
/* Decode more bits each time ... */
for (extra_nbits=0; extra_nbits<16-HUFFMAN_HASH_NBITS; extra_nbits++)
{
nbits = HUFFMAN_HASH_NBITS + 1 + extra_nbits;
//读取nbits
look_nbits(priv->reservoir, priv->nbits_in_reservoir, priv->stream, nbits, hcode);
slowtable = huffman_table->slowtable[extra_nbits];
/* Search if the code is in this array */
while (slowtable[0]) {
if (slowtable[0] == hcode) {
skip_nbits(priv->reservoir, priv->nbits_in_reservoir, priv->stream, nbits);
return slowtable[1];
}
slowtable+=2;
}
}
return 0;
2 对每个宏块的 DCT 系数进行 IDCT,得到 Y、Cb、Cr
void tinyjpeg_idct_float (struct component *compptr, uint8_t *output_buf, int stride)
{
FAST_FLOAT tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7;
FAST_FLOAT tmp10, tmp11, tmp12, tmp13;
FAST_FLOAT z5, z10, z11, z12, z13;
int16_t *inptr;
FAST_FLOAT *quantptr;
FAST_FLOAT *wsptr;
uint8_t *outptr;
int ctr;
FAST_FLOAT workspace[DCTSIZE2]; /* buffers data between passes */
/* Pass 1: process columns from input, store into work array. */
inptr = compptr->DCT;
quantptr = compptr->Q_table;
wsptr = workspace;
for (ctr = DCTSIZE; ctr > 0; ctr--) {
/* Due to quantization, we will usually find that many of the input
* coefficients are zero, especially the AC terms. We can exploit this
* by short-circuiting the IDCT calculation for any column in which all
* the AC terms are zero. In that case each output is equal to the
* DC coefficient (with scale factor as needed).
* With typical images and quantization tables, half or more of the
* column DCT calculations can be simplified this way.
*/
if (inptr[DCTSIZE*1] == 0 && inptr[DCTSIZE*2] == 0 &&
inptr[DCTSIZE*3] == 0 && inptr[DCTSIZE*4] == 0 &&
inptr[DCTSIZE*5] == 0 && inptr[DCTSIZE*6] == 0 &&
inptr[DCTSIZE*7] == 0) {
/* AC terms all zero */
FAST_FLOAT dcval = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]);
wsptr[DCTSIZE*0] = dcval;
wsptr[DCTSIZE*1] = dcval;
wsptr[DCTSIZE*2] = dcval;
wsptr[DCTSIZE*3] = dcval;
wsptr[DCTSIZE*4] = dcval;
wsptr[DCTSIZE*5] = dcval;
wsptr[DCTSIZE*6] = dcval;
wsptr[DCTSIZE*7] = dcval;
inptr++; /* advance pointers to next column */
quantptr++;
wsptr++;
continue;
}
3 遇到 Segment Marker RST 时,清空之前的 DC DCT 系数
五 解析到 EOI ,解码结束
六 将 Y 、Cb 、Cr 转化为需要的色彩空间并保存。
任务
任务一输出YUV文件
static void write_yuv(const char *filename, int width, int height, unsigned char **components)
{
FILE *F;
char temp[1024];
snprintf(temp, 1024, "%s.Y", filename);
F = fopen(temp, "wb");
fwrite(components[0], width, height, F);
fclose(F);
snprintf(temp, 1024, "%s.U", filename);
F = fopen(temp, "wb");
fwrite(components[1], width*height/4, 1, F);
fclose(F);
snprintf(temp, 1024, "%s.V", filename);
F = fopen(temp, "wb");
fwrite(components[2], width*height/4, 1, F);
fclose(F);
//添加代码
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);
}
任务二理解三个结构体的设计目的
理解三个结构体的设计目的
struct huffman_table//建立huffman表
{
/* 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,slowtable处理
};
struct component //当前正在处理的8×8的块
{
unsigned int Hfactor; //水平采样因子
unsigned int Vfactor; //垂直采样因子
float *Q_table; /* Pointer to the quantisation table to use */
struct huffman_table *AC_table;//交流系数的huffman表
struct huffman_table *DC_table;//直流系数的huffman表
short int previous_DC; /* Previous DC coefficient */
short int DCT[64]; /* DCT coef *///当前块的DCT系数,DCT[0]为直流系数,DCT[1~63]为交流系数
#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];
jmp_buf jump_state;
/* Internal Pointer use for colorspace conversion, do not modify it !!! */
uint8_t *plane[COMPONENTS];
};
理解在视音频编解码调试中TRACE的目的和含义
#define snprintf _snprintf//add by nxn
#define TRACE 1//1为打开,0为关闭
#define TRACEFILE "trace_jpeg.txt"//输出文件名
修改TRACE:
#if TRACE
fprintf(p_trace,"val=%2.2x code=%8.8x codesize=%2.2d\n", val, code, code_size);
fflush(p_trace);
fprintf(huffman_fp, "val=%2.2x code=%8.8x codesize=%2.2d\n", val, code, code_size);
fflush(huffman_fp);
#endif
在类似if TRACE和end if中间修改代码即可修改TRACE
TRACE的目的及含义:输出了图片解析过程的每一步结果(验证标识符、图像信息、huffman表),便于调试代码
任务三以txt文件输出所有的量化矩阵和所有的HUFFMAN码表
tinyjpeg.h:
FILE* huffman_fp;
FILE* qtable_fp;
int main(int argc, char *argv[])
{
……
//添加代码
huffman_fp=fopen("huffman.txt", "w");
if (huffman_fp==NULL) {
printf("fali to open huffman.txt");
exit(0);
}
qtable_fp=fopen("qtable.txt", "w");
if (qtable_fp==NULL) {
printf("fail to open qtable.txt");
exit(0);
}
……
fclose(huffman_fp);
fclose(qtable_fp);
return 0;
}
量化表输出
static int parse_DQT(struct jdec_private *priv, const unsigned char *stream)
{
……
table = priv->Q_tables[qi];
fprintf(qtable_fp, "量化表[%d]\n",qi); //输出量化表
build_quantization_table(table, stream);//建立量化表
fprintf(qtable_fp, "\n");
stream += 64;//量化表长度固定为64bytes
}
static void build_quantization_table(float *qtable, const unsigned char *ref_table)
{
int i, j;
static const double aanscalefactor[8] = {
1.0, 1.387039845, 1.306562965, 1.175875602,
1.0, 0.785694958, 0.541196100, 0.275899379
};
const unsigned char *zz = zigzag;
//添加代码
for (i=0; i<8; i++) {
for (j=0; j<8; j++) {
*qtable++ = ref_table[*zz++] * aanscalefactor[i] * aanscalefactor[j];
fprintf(qtable_fp, "%-8d", ref_table[*zz]);
if (j == 7)
fprintf(qtable_fp, "\n");
}
}
}
huffman表输出
static void build_huffman_table(const unsigned char *bits, const unsigned char *vals, struct huffman_table *table)
{
……
#if TRACE
fprintf(p_trace,"val=%2.2x code=%8.8x codesize=%2.2d\n", val, code, code_size);
fflush(p_trace);
//添加代码
fprintf(huffman_fp, "val=%2.2x code=%8.8x codesize=%2.2d\n", val, code, code_size);
fflush(huffman_fp);
#endif
……
}
任务四输出AC、DC图像并统计其概率分布
tinyjpeg.h:
FILE* DC_image;
FILE* AC_image;
int main(int argc, char *argv[])
{
……
//添加代码
DC_image = fopen("DC.yuv", "wb");
if (DC_image == NULL) {
printf("fail to open DC.yuv");
exit(0);
}
AC_image = fopen("AC.yuv", "wb");
if (AC_image == NULL) {
printf("fail to open AC.yuv");
exit(0);
}
……
fclose(DC_image);
fclose(AC_image);
return 0;
}
int tinyjpeg_decode(struct jdec_private *priv, int pixfmt)
{
unsigned int x, y, xstride_by_mcu, ystride_by_mcu;
unsigned int bytes_per_blocklines[3], bytes_per_mcu[3];
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;
unsigned char* dcbuff, * acbuff;
unsigned char* uvbuff = 128;
double dt[256] = { 0 }, at[256] = { 0 };
int count = 0;
……
/* 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)
{
decode_MCU(priv);
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];
dcbuff = (unsigned char)((priv->component_infos->DCT[0] + 512.0) / 4 + 0.5);//将DC系数范围从-512到512变为0到256
unsigned char temp1 = dcbuff;
dt[temp1]++;
acbuff = (unsigned char)(priv->component_infos->DCT[1] + 128);
unsigned temp2 = acbuff;
at[temp2]++;
fwrite(&dcbuff, 1, 1, DC_image);
fwrite(&acbuff, 1, 1, AC_image);
count++;
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;
}
}
}
}
#if TRACE
fprintf(p_trace,"Input file size: %d\n", priv->stream_length+2);
fprintf(p_trace,"Input bytes actually read: %d\n", priv->stream - priv->stream_begin + 2);
fflush(p_trace);
#endif
for (int i = 0; i < count * 2; i++) {
fwrite(&uvbuff, sizeof(unsigned char), 1, DC_image);
fwrite(&uvbuff, sizeof(unsigned char), 1, AC_image);
}
double df[256] = { 0 },af[256] = { 0 };
for (int i = 0; i < 256; i++) {
df[i] = dt[i] / (double)count;
//fprintf(huffman_fp, "val=%2.2x code=%8.8x codesize=%2.2d\n", val, code, code_size);
fprintf(DC_TXT,"%d\t%10.6f\n", i,df[i]);
fprintf(DC_image,"\n");
af[i] = at[i] / (double)count;
fprintf(AC_TXT,"%d\t%10.6f\n",i, af[i]);
fprintf(AC_TXT,"\n");
}
return 0;
}
DC:
AC: