JPEG编解码原理

一.JPEG简介

JPEG全称Joint Photographic Experts Group(联合图像专家组),它是一项数字图像压缩标准(ISO/IEC 10918),1992年提出。
JPEG是一种有损压缩的数字图像技术,它的核心算法是离散余弦变换(DCT)。

二.JPEG压缩技术

JPEG编码原理涉及到一些图像处理的知识,强烈推荐先看一下:图像与滤波

JPEG编码过程如下图
[图片上传失败...(image-c3fa31-1583587032871)]
解码过程就是编码的逆向操作

[图片上传失败...(image-2ad7ec-1583587032871)]

2.1 块切割

JPEG标准在处理图片时会先把图片分割成一个个8x8像素的方块,后面的DCT、量化、熵编码都是针对单个方块的操作,编码的产物是这些方块的压缩数据。压缩数据经过解码还原成像素数据,然后将一个个方块拼成一整完整图片的像素数据送给显卡展示。

2.2 DCT

DCT(Discrete Cosine Transform)变换的全称是离散余弦变换,它能将时域信号转换成频域信号,其中低频部分集中在矩阵的左上角,高频部分集中在矩阵的右下角。它是傅里叶变换的一种变种,相比傅里叶变换,DCT变换函数只用实数,算法实现简单,所以广泛运用在图片压缩领域,JPEG就采用了二维DCT变换。

由于人眼对图片中的低频信息(色彩变化不明显,如图片的整体色调,物体轮廓)比较敏感,对高频信息不敏感(色彩变化剧烈,如物体的边缘、人脸上的小斑点),因此我们可以利用DCT变换把图片中高频和低频部分区分开来,然后将高频部分的数据进行压缩,这样就达到压缩图片的功能。

二维DCT变换的公式为:

image
  • F(u, v) 代表DCT变换后坐标(u, v)的频率
  • c(u)、c(v)可以认为是一个补偿系数,可以使DCT变换矩阵为正交矩阵
  • f(i, j) 代表坐标(i, j)的像素数据



假如我们有一个8x8的像素数据:

image

运用DCT公式,我们把像素信号转换成如下频率信号:

image

左上区域存储的是低频信号,右下区域存储的是高频信号

2.2 量化

下面我们用一个50% quantily的JPEG量化表将频率数据量化:

image

量化公式如下:

  • B(i, j) = round(G(i, j) / Q(i, j))

如:Q(0, 0) = round(-415.38 / 16) = -26
Q(0, 1) = round(-30.19 / 11) = -3

所谓量化其实就是将频率/量化步长,将量化步长以内的精度信息丢失。可以观察到上表的左上角数值小,右下角数值大,因此这张量化表的作用就是屏蔽高频信息。最终的量化结果为:

image

2.3 熵编码

熵编码是一类编码规范,它要求编码过程中按熵原理不丢失任何信息。常见的熵编码有:香农编码、哈夫曼编码和算术编码。
JPEG先用RLE(run-length encoding,游程编码)编码将图像数据以“之字形”排列,如下图,这样可以尽可能的将频率为0的数据存储在一起。连续N个0,可以用一个0和一个长度N来表示,压缩效果很好,然后将剩下的位置使用霍夫曼编码。

image

以上就是JPEG编码的重要步骤。解码基本上就是上述步骤的逆向操作,就不多说了,这里只介绍一下IDCT

2.4 IDCT

IDCT即Inverse DCT,它就是DCT的逆向操作,将图像的频率数据转换成像素数据,公式如下:

image
  • f(i, j) 坐标(i, j)的像素数据
  • F(u, v) 坐标(u, v)的频率数据
  • c(u)、c(v)可以认为是一个补偿系数,可以使DCT变换矩阵为正交矩阵

还是看上面的例子,我们先做一下反量化操作,即乘一下步长就可以了:

image

接着我们将上图的频率数据代入IDCT公式,最终我们得到还原后的像素数据:

image

三.Android实例

下面我们通过一个Android实例来看一下DCT转换的效果
为了方便理解,我这里用一张Y分量的yuv图片来演示,原图如下

image

二维DCT变换公式其实是一个矩阵变换公式,上面的公式是它的求和形式,效率比较低。开发中一般直接用矩阵运算替代,公式如下:

image
image

其中X为yuv像素矩阵,Y为频域信号矩阵

3.1 DCT变换

这里引用了apache的commons-math3库来做矩阵运算

    // dct变换
    public static RealMatrix dct2(byte[] yuv, int N) {
        // 拷贝N*N的Y分量
        byte[] y = new byte[N * N];
        System.arraycopy(yuv, 0, y, 0, y.length);
        // 一维数组转成二维数组
        double[][] matrixData = MatrixUtils.toMatrixData(y, N);
        // 构造yuv矩阵
        RealMatrix signalMatrix = new Array2DRowRealMatrix(matrixData);
        // 获取dct系数矩阵
        RealMatrix dctMatrix = getDCTMatrix(N);
        // 频域矩阵 = dct系统矩阵 * yuv矩阵 * dct系数转置矩阵 
        RealMatrix frequencyMatrix = dctMatrix.multiply(signalMatrix).multiply(dctMatrix.transpose());
        return frequencyMatrix;
    }
    
    // 获取dct系数矩阵
    private static RealMatrix getDCTMatrix(int N) {
        double matrixData[][] = new double[N][N];
        for (int i = 0; i < N; i++) {
            for (int j = 0; j < N; j++) {
                double factor = i == 0 ? Math.sqrt(1d / N) : Math.sqrt(2d / N);
                matrixData[i][j] = factor * Math.cos((2 * j + 1) * i * Math.PI * 0.5d / N);
            }
        }
        return new Array2DRowRealMatrix(matrixData);
    }    

变换之后效果如下:

image

3.2 IDCT变换

DCT反变换公式如下:

image
 // dct反变换
 public static double[][] idct2(RealMatrix frequencyMatrix, int N) {
        // 获取dct系数矩阵
        RealMatrix dctMatrix = getDCTMatrix(N);
        // 频域矩阵 = dct系数转置矩阵 * dct系统矩阵 * yuv矩阵
        RealMatrix frequencyMatrix = dctMatrix.transpose().multiply(frequencyMatrix).multiply(dctMatrix);
        return signalMatrix;
    }

变换效果就是把上面的频域信号转成yuv信号,效果就跟原图一样(DCT变换过程是无损的,忽略运算过程中的误差)

3.3 分块DCT变换

将图片分为一个个8x8的方块,分别对这些方块做dct变换,代码就不展示了,直接看效果

image

3.4 量化

这里依然用JPEG 50% quantily量化表,效果如下

image

3.5 分块反DCT变换

将量化后的一个个8x8方块依次做IDCT变换,再拼成一整完整的图片


image

左边是原图,右边是量化之后的图,仔细看还是能发现右图有一些毛边,细节上不如左图清晰

项目地址:
Gitee:https://gitee.com/huaisu2020/Android-Live
Github:https://github.com/xh2009cn/Android-Live

参考文章:
http://www.ruanyifeng.com/blog/2017/12/image-and-wave-filters.html
https://en.wikipedia.org/wiki/JPEG

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

推荐阅读更多精彩内容

  • * 音视频入门文章目录 *[https://www.ihubin.com/blog/audio-video-bas...
    binglingziyu阅读 619评论 0 2
  • JPEG文件的存储格式有很多种,但最常用的是JFIF格式,即JPEG File Interchange Forma...
    hehtao阅读 8,725评论 1 7
  • (文章还剩实践部分没写,答辩过后补上...)JPEG文件在当下数字化生活中是无处不在的,但是在熟悉的JPEG面纱背...
    MrYun阅读 332评论 0 0
  • ### YUV颜色空间 视频是由一帧一帧的数据连接而成,而一帧视频数据其实就是一张图片。 yuv是一种图片储存格式...
    天使君阅读 3,228评论 0 4
  • 姓名:李伟 学号:17101223393 【嵌牛导读】:大家对JPEG了解吗?今天和大家分享一下。 【嵌牛鼻子...
    LW_e565阅读 2,806评论 0 1