更新博客内容

This commit is contained in:
2025-02-22 19:54:50 +08:00
parent a1a4d970af
commit bfc428b237
90 changed files with 3370 additions and 8638 deletions
+124
View File
@@ -0,0 +1,124 @@
---
title: "JPEG算法解密(二)"
tags: 压缩 图像 程序 算法
---
# JPEG算法解密(二)
### 步骤三:离散余弦变换
----
这次我们来介绍JPEG算法中的核心内容,离散余弦变换(Discrete cosine transform),简称DCT。
离散余弦变换属于傅里叶变换的另外一种形式,没错,就是大名鼎鼎的傅里叶变换。傅里叶是法国著名的数学家和物理学家,1807年,39岁的傅里叶在他的一篇论文里提出了一个想法,他认为*任何周期性的函数,都可以分解为为一系列的三角函数的组合*,这个想法一开始并没有得到当时科学界的承认,比如当时著名的数学家拉格朗日提出质疑,三角函数无论如何组合,都无法表达带有“尖角”的函数,一直到1822年拉格朗日死后,傅里叶的想法才正式在他的著作《热的解析理论》一书中正式发表。
![](/images/2014/08/jpeg_20.jpg)
金子总会闪光,傅里叶变换如今广泛应用于数学、物理、信号处理等等领域,变换除了它在数学上的意义外,还有其哲学上的伟大意义,那就是,世上任何复杂的事物,都可以分解为简单的事物的组合,而这个过程只需要借助数学工具就可以了。但是当年拉格朗日的质疑是正确的,三角函数的确无法表达出尖角形状的函数,不过只要三角函数足够多,可以无限逼近最终结果。比如下面这张动图,就动态描述了一个矩形方波,是如何做傅里叶分析的。
<p align="center">
<img src="/images/2014/08/jpeg_22.jpg" width="45%">
<img src="/images/2014/08/jpeg_21.gif" width="45%">
</p>
当我们要处理的不再是函数,而是一堆离散的数据时,并且这些数据是对称的话,那么傅里叶变化出来的函数只含有余弦项,这种变换称为离散余弦变换。举个例子,有一组一维数据$[x_0,x_1,x_2,\ldots,x_{n-1}]$,那么可以通过DCT变换得到$n$个变换级数$F_i$
$$\begin{aligned}
F_m&=\sum_{k=0}^{n-1}x_k\cos\left[\frac{\pi}{n}m(k+\frac{1}{2})\right] \\
m&=0,1,\ldots,n-1
\end{aligned}\tag{2.1}
$$
此时原始数据$x_m$可以通过离散余弦变换变化的逆变换(IDCT)表达出来
$$\begin{aligned}
x_m&=\frac{F_0}{n}+\sum_{k=1}^{n-1}\left[\frac{2F_k}{n}\cos\left[\frac{\pi}{n}(m+\frac{1}{2})k\right]\right]\\
m&=0,1,\ldots,n-1
\end{aligned}\tag{2.2}
$$
也就是说,经过DCT变换,可以把一个数组分解成数个数组的和,如果我们数组视为一个一维矩阵,那么可以把结果看做是一系列矩阵的和
$$\begin{aligned}
&\left[x_0,x_1,x_2,\ldots,x_{n-1}\right]=\frac{F_0}{n}\left[1,1,1,\ldots,1\right]\\
&+\small{\frac{2F_1}{n}\left[\cos{\frac{\pi}{2n}},\cos{\frac{3\pi}{2n}},\cos{\frac{5\pi}{2n}},\ldots,\cos{\frac{(2n-1)\pi}{2n}}\right]}\\
&+\small{\frac{2F_2}{n}\left[\cos{\frac{2\pi}{2n}},\cos{\frac{6\pi}{2n}},\cos{\frac{10\pi}{2n}},\ldots,\cos{\frac{2(2n-1)\pi}{2n}}\right]}\\
&+\small{\frac{2F_3}{n}\left[\cos{\frac{3\pi}{2n}},\cos{\frac{9\pi}{2n}},\cos{\frac{15\pi}{2n}},\ldots,\cos{\frac{3(2n-1)\pi}{2n}}\right]}\\
&+\ldots\\
&+\small{\frac{2F_{n-1}}{n}\left[\cos{\frac{(n-1)\pi}{2n}},\cos{\frac{2(n-1)\pi}{2n}},\cos{\frac{3(n-1)\pi}{2n}},\ldots,\cos{\frac{(n-1)(2n-1)\pi}{2n}}\right]}\\
\end{aligned}\tag{2.3}
$$
举个例子,我们有一个长度为8的数字,内容为[50,55,67,80,-10,-5,20,30],在数轴上展示的话,没有任何明显的规律
![](/images/2014/08/jpeg_25.gif "[50,55,67,80,-10,-5,20,30]")
经过DCT转换,得到8个级数为[287.0,106.3,14.2,-110.8,9.2,65.7,-8.2,-43.9],根据公式2.3把这个数组转换为8个新的数组的和,再使用图像来表达的话,就可以发现DCT转换的有趣之处了
![](/images/2014/08/jpeg_26.gif "数组0:[35.9,35.9,35.9,35.9,35.9,35.9,35.9,35.9]")
![](/images/2014/08/jpeg_27.gif "数组1:[26.0,22.1,14.8,5.2,-5.2,-14.8,-22.1,-26.1]")
![](/images/2014/08/jpeg_28.gif "数组2:[3.3,1.4,-1.4,-3.3,-3.3,-1.4,1.4,3.3]")
![](/images/2014/08/jpeg_29.gif "数组3:[-23.0,5.4,27.2,15.4,-15.4,-27.2,-5.4,23.0]")
![](/images/2014/08/jpeg_30.gif "数组4:[1.6,-1.6,-1.6,1.6,1.6,-1.6,-1.6,1.6]")
![](/images/2014/08/jpeg_31.gif "数组5:[9.1,-16.1,3.2,13.6,-13.6,-3.2,16.1,-9.1]")
![](/images/2014/08/jpeg_32.gif "数组6:[-0.8,1.9,-1.9,0.8,0.8,-1.9,1.9,-0.8]")
![](/images/2014/08/jpeg_33.gif "数组7:[-2.1,6.1,-9.1,10.8,-10.8,9.1,-6.1,2.1]")
奥妙之处在于,经过DCT,数据中隐藏的规律被发掘了出来,杂乱的数据被转换成几个工整变化的数据。DCT转换后的数组中第一个是一个直线数据,因此又被称为“直流数据”,简称DC,后面的数据被称为“交流数据”,简称AC,这个称呼起源于信号分析中的术语。
在JPEG压缩过程中,经过颜色空间的转换,每一个8X8的图像块,在数据上表现为3个8X8的矩阵,紧接着我们对这三个矩阵做一个二维的DCT转换,二维的DCT转换公式为
$$\small{\begin{aligned}
F(u,v)&=\alpha(u)\cdot\alpha(v)\cdot \sum_{x=0}^{7}\sum_{y=0}^{7}f(x,y)\cos\left(\frac{2x+1}{16}u\pi\right)\cos\left(\frac{2y+1}{16}v\pi\right)\quad u,v=0,1,2,\ldots,7\\
\alpha(u)&=\begin{cases}1/\sqrt{8}, & \text{when u}=0\\
1/2, & \text{when u}\neq 0\end{cases}
\end{aligned}}\tag{2.4}
$$
DCT的威力究竟有多大,我们可以做一个实际的测试,比如一个所有数值都一样的矩阵,经过DCT转换后,将所有级数组合成一个新的矩阵
$$
\small{\begin{bmatrix}
100&100&100&100&100&100&100&100\\
100&100&100&100&100&100&100&100\\
100&100&100&100&100&100&100&100\\
100&100&100&100&100&100&100&100\\
100&100&100&100&100&100&100&100\\
100&100&100&100&100&100&100&100\\
100&100&100&100&100&100&100&100\\
100&100&100&100&100&100&100&100
\end{bmatrix}\overset{\text{DCT}}{\Longrightarrow}
\begin{bmatrix}
800&0&0&0&0&0&0&0\\
0&0&0&0&0&0&0&0\\
0&0&0&0&0&0&0&0\\
0&0&0&0&0&0&0&0\\
0&0&0&0&0&0&0&0\\
0&0&0&0&0&0&0&0\\
0&0&0&0&0&0&0&0\\
0&0&0&0&0&0&0&0
\end{bmatrix}
}
$$
可以看到,经过DCT转换,矩阵的“能量”被全部集中在左上角上的直流分量F(0,0)上,其他位置都变成了0。
在实际的JPEG压缩过程中,由于图像本身的连贯性,一个8X8的图像中的数值一般不会出现大的跳跃,经过DCT转换会有类似的效果,左上角的直流分量保存了一个大的数值,其他分量都接近于0,我们以Lenna左上角第一块图像的Y分量为例,经过变换的矩阵为
$$
\small{\begin{bmatrix}
34&34&34&33&34&28&35&32\\
34&34&34&33&34&28&35&32\\
34&34&34&33&34&28&35&32\\
34&34&34&33&34&28&35&32\\
34&34&34&33&34&28&35&32\\
36&36&29&27&33&31&30&31\\
32&32&35&30&32&33&31&27\\
30&30&27&28&30&30&28&29\\
\end{bmatrix}\overset{\text{DCT}}{\Longrightarrow}
\begin{bmatrix}
257.1&6.4&2.5&-0.3&0.4&0.1&-6.0&6.9\\
8.4&0.0&0.5&-0.5&1.9&3.4&-4.2&3.3\\
-5.3&-1.0&-1.4&1.3&-0.7&-0.5&2.1&-1.7\\
2.4&1.7&1.5&1.5&-0.6&-1.5&0.2&0.4\\
-1.1&-1.6&-0.2&-1.8&1.6&1.2&-1.4&-0.1\\
1.4&0.9&-1.9&-0.1&-2.0&0.9&1.5&0.7\\
-2.0&-0.1&3.1&2.0&1.8&-2.7&-0.9&-1.3\\
1.5&-0.2&-2.3&-1.9&-1.0&2.3&0.3&1.1\\
\end{bmatrix}
}
$$
可以看到,数据经过DCT变化后,被明显分成了直流分量和交流分量两部分,为后面的进一步压缩起到了充分的铺垫作用,可以说是整个JPEG中最重要的一步,后面我们会介绍数据量化。