jpeg编码过程

了解学习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]

1.jpg.png

图片来源于网络

色度抽样

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]

2.jpg.png

量化

所谓量化就是用像素值÷量化表对应值所得的结果。由于量化表左上角的值较小,右上角的值较大,这样就起到了保持低频分量,抑制高频分量的目的。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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,539评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,594评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,871评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,963评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,984评论 6 393
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,763评论 1 307
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,468评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,357评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,850评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,002评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,144评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,823评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,483评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,026评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,150评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,415评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,092评论 2 355