了解学习jpeg编码过程
阅读学习的代码来源:https://github.com/rockcarry/ffjpeg.git
代码整体结构
if (strcmp(argv[1], "-d") == 0) {//解码
jfif = jfif_load(argv[2]);//加载文件
jfif_decode(jfif, &bmp);//文件解码
jfif_free (jfif);//删除jfif对象
bmp_save(&bmp, "decode.bmp");
bmp_free(&bmp);
printf("%d",12);
} else if (strcmp(argv[1], "-e") == 0) {//bmp编码成jpg
bmp_load(&bmp, argv[2]);//加载bmp图片
jfif = jfif_encode(&bmp);
bmp_free(&bmp);
jfif_save(jfif, "encode.jpg");
jfif_free(jfif);
}
这里阐述jpeg编码过程,我们需要首先了解一下jpg的基本内容,JPEG图片格式组成部分有SOI(文件头)+APP0(图像识别信息)+ DQT(定义量化表)+ SOF0(图像基本信息)+ DHT(定义Huffman表) + DRI(定义重新开始间隔)+ SOS(扫描行开始)+ EOI(文件尾)[i]
图片来源于网络
色度抽样
JPEG 文件是由 YUV 的色彩空间来表示颜色的,YUV 中 Y 表示明亮度(Luma),U 和 V 表示色度(Chrominance)和浓度(Chroma),UV 分量同时表示色差[ii]。
首先对原图像进行RGB 到 YUV 进行4:2:0色度抽样。这一步有损,若直接由RGB转到YUV,这一步无损。
https://blog.csdn.net/leansmall/article/details/84262091 这个网站上介绍了几种颜色空间的不同,以及彼此的采样方式与两两之间的映射方式。
YUV和RGB的转换:
Y = 0.299 R + 0.587 G + 0.114 B
U = -0.1687 R - 0.3313 G + 0.5 B + 128
V = 0.5 R - 0.4187 G - 0.0813 B + 128
在代码中的实现方式如下:
void rgb_to_yuv(BYTE r, BYTE g, BYTE b, int *y, int *u, int *v)
{
*y = FLOAT2FIX( 0.2990f) * r + FLOAT2FIX(0.5870f) * g + FLOAT2FIX(0.1140f) * b - (128 << FIXQ);
*u = FLOAT2FIX(-0.1678f) * r - FLOAT2FIX(0.3313f) * g + FLOAT2FIX(0.5000f) * b;
*v = FLOAT2FIX( 0.5000f) * r - FLOAT2FIX(0.4187f) * g - FLOAT2FIX(0.0813f) * b;
*y >>= FIXQ - 2;//FIXQ 11
*u >>= FIXQ - 2;
*v >>= FIXQ - 2;
}
这里应该是间隔采样过程
// convert rgb to yuv
bsrc = pb->pdata;
ydst = yuv_datbuf[0];
udst = yuv_datbuf[1];
vdst = yuv_datbuf[2];
for (i=0; i<pb->height; i++) {
for (j=0; j<pb->width; j++) {
rgb_to_yuv(bsrc[2], bsrc[1], bsrc[0], ydst, udst, vdst);
bsrc += 3;
ydst += 1;//全采样
if (j & 1) {
udst += 1;
vdst += 1;
}
}
bsrc -= pb->width * 3; bsrc += pb->stride;
ydst -= pb->width * 1; ydst += jw;
udst -= pb->width / 2;
vdst -= pb->width / 2;
if (i & 1) {//按位与,对应位是否为1 。width间隔采样
udst += jw / 2;
vdst += jw / 2;
}
}
预处理****块划分
对图片进行8×8分块,方便处理。
for (m=0; m<jh/16; m++) {
for (n=0; n<jw/16; n++) {
//++ encode mcu, yuv 4:2:0
//+ y du0
isrc = yuv_datbuf[0] + (m * 16 + 0) * jw + n * 16 + 0;
idst = du;
for (i=0; i<8; i++) {
memcpy(idst, isrc, 8 * sizeof(int));
isrc += jw; idst += 8;
}
jfif_encode_du(jfif, DU_TYPE_LUMIN, du, &(dc[0]));
//- y du0这里放到一个这样的循环里对图片进行处理。
DCT 离散余弦变换
主要作用是使能量集中在左上角
DCT变换的全称是离散余弦变换(Discrete Cosine Transform),主要用于将数据或图像的压缩,能够将空域的信号转换到频域上,具有良好的去相关性的性能。DCT变换本身是无损的,但是在图像编码等领域给接下来的量化、哈弗曼编码等创造了很好的条件,同时,由于DCT变换时对称的,所以,我们可以在量化编码后利用DCT反变换,在接收端恢复原始的图像信息[iii]。
量化
所谓量化就是用像素值÷量化表对应值所得的结果。由于量化表左上角的值较小,右上角的值较大,这样就起到了保持低频分量,抑制高频分量的目的。JPEG使用的颜色是YUV格式。我们提到过,Y分量代表了亮度信息,UV分量代表了色差信息。相比而言,Y分量更重要一些。我们可以对Y采用细量化,对UV采用粗量化,可进一步提高压缩比[iv]。所以上面所说的量化表通常有两张,一张是针对Y的;一张是针对UV的。
如下是两个不同的量化表,针对Y和UV
/* 全局变量定义 */
const int STD_QUANT_TAB_LUMIN[64] =
{
16, 11, 10, 16, 24, 40, 51, 61,
12, 12, 14, 19, 26, 58, 60, 55,
14, 13, 16, 24, 40, 57, 69, 56,
14, 17, 22, 29, 51, 87, 80, 62,
18, 22, 37, 56, 68, 109,103,77,
24, 35, 55, 64, 81, 104,113,92,
49, 64, 78, 87, 103,121,120,101,
72, 92, 95, 98, 112,100,103,99,
};
const int STD_QUANT_TAB_CHROM[64] =
{
17, 18, 24, 47, 99, 99, 99, 99,
18, 21, 26, 66, 99, 99, 99, 99,
24, 26, 56, 99, 99, 99, 99, 99,
47, 66, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
};
/* 函数实现 */
void quant_encode(int du[64], int qtab[64])
{
int i; for (i=0; i<64; i++) du[i] /= qtab[i];
}
void quant_decode(int du[64], int qtab[64])
{
int i; for (i=0; i<64; i++) du[i] *= qtab[i];
}
量化进行了除法,有压缩,也有损失。
编码分类
编码信息分为类 [0,0]位置上元素,这是DC(直流分量),代表8*8个子块的平均值,JPEG中对F[0,0]单独的差分编码。而对另外63个子块进行zigzag编码,为了保证低频分量先出现,高频分量后出现。
霍夫曼编码
对DC分量和AC分量分别进行霍夫曼编码。
// <u>huffman</u> encode for <u>dc</u>
huffman_encode_step(hfcdc, size);
bitstr_put_bits(bs, code, size);
// <u>huffman</u> encode for <u>ac</u>
**for** (i=0; i<j; i++) {
huffman_encode_step(hfcac, (rlelist[i].runlen << 4) | (rlelist[i].codesize << 0));
bitstr_put_bits(bs, rlelist[i].codedata, rlelist[i].codesize);
}
文件封装
最后把上述信息整合成一整个jpeg文件。
这个代码里实现过程是先是创建一个jpeg对象,初始化jpeg文件相关信息,如宽高、量化表、霍夫曼边等。
参考文献
[i] https://blog.csdn.net/yun_hen/article/details/78135122
[ii] 影像算法解析——JPEG 压缩算法 - 如云般飘过的文章 - 知乎 https://zhuanlan.zhihu.com/p/40356456
[iii] https://blog.csdn.net/weixin_30609331/article/details/98157347
[iv] https://blog.csdn.net/my_happy_life/article/details/82997597