二维离散余弦变换(python实战)
(注:文中所讨论的离散余弦变换都是DCT-II)
在上一篇文章中,我详细介绍了一维离散余弦变换的基本原理,并且以可视化的方式展示了一维DCT中用于分析输入信号的一系列基础余弦波。 在这篇文章中继续关于DCT的学习,并把DCT从一维推广到二维,最后以图像的离散余弦变换为结尾。
二维DCT正变换
二维DCT正变换公式如下:
或
其中,x[m,n]为输入图像的像素值,m,n=0,1,...,N-1。X[]为经过离散余弦变换后的结果,即离散余弦变换的系数,,=0,1,...,N-1。E(),E()为归一化系数,当=0时, E(),E()=。 当 ,=1,2,3,....,N-1时, E(),E()=1。
单从公式来看,我们可以知道三点。第一,频域中的每一个点X[]都要遍历整幅图像计算。二,就某个固定的,而言,分析每行和每列数据用的是同一组cos函数。第三,越是往频域的右下角计算,分析图像所需的cos函数的频率就越高。
以4个点的X[0,0]的计算为例,N=4,=0:
这个公式表明,要想求出X[0,0]。要先让原始二维信号x[m,n]的每一行乘以幅值为1/2频率为0的余弦信号[0.5,0.5,0.5,0.5],然后再用这个结果乘以余弦信号[0.5,0.5,0.5,0.5]才能得到最终结果。下面是基于python的实现,主要是用于验证计算结果并演示计算过程:
#生成随机二维信号
x2=np.random.randn(4,4)
x2
导入opencv的dct库,计算二维DCT。后面会根据我上面推导出来的公式,计算X[0,0],并用CV库的计算结果来验证。
import cv2
dct_matrix=cv2.dct(x2)
dct_matrix
以下是基于上面的推导结果求得的X[0,0],整个计算过程是先用幅值为1/2频率为0的cos函数乘以二维数组的每一行并得到一个中间结果,最后用这个中间结果再次乘以cos函数得到最终结果,这一计算过程和推导一致:
A=DCT1d_matrix(4)
print("Forward transform matrix A:\n",A)
#原始信号的每一行乘以余弦信号,得到一个中间结果
step1=x2@A[0,:]
print("result of step1:",step1)
#中间结果再度乘以余弦信号,得到最终结果
step2=step1@A[0,:]
print("result of step2:",step2)
根据计算结果来看,1,计算时所使用的cos函数为一维DCT矩阵的第一行。毕竟DCT矩阵中的每一行都是一个固定频率的cos函数。2,按照这种方法计算出来的最终结果0.8999和用cv库算出来的左上角第一个值一致。
0,0点的例子比较特殊,现在我们再看一个点X[1,2]。还是4个点的DCT,N=4,,:
结合推导结果来看,要想得到X[1,2]的结果。先是要让原始二维信号的每一行都乘以频率为2/4pi的cos函数得到中间结果,这一步所使用的cos函数为:
此时,然后再用这个中间结果乘以频率为1/4pi的cos函数得到最终结果。这一步所使用的cos函数为:
此时,实际上这两组cos函数就是一维4点DCT变换矩阵其中的两行。
cos0=np.array([np.sqrt(1/2)*np.cos(2*np.pi/8),np.sqrt(1/2)*np.cos(6*np.pi/8),np.sqrt(1/2)*np.cos(10*np.pi/8),np.sqrt(1/2)*np.cos(14*np.pi/8)])
print("cos with freq of 2/4pi:",cos0)
cos1=np.array([np.sqrt(1/2)*np.cos(1*np.pi/8),np.sqrt(1/2)*np.cos(3*np.pi/8),np.sqrt(1/2)*np.cos(5*np.pi/8),np.sqrt(1/2)*np.cos(7*np.pi/8)])
print("cos with freq of 1/4pi:",cos1)
最后再次基于推导结果计算X[1,2],先用时的cos函数乘以二维信号的每一行,得到中间结果。然后再用时的cos函数乘以中间结果,得到最终结果:
#原始信号的每一行乘以nu=2时的余弦信号,得到一个中间结果
step1=x2@A[2,:]
print("result of step1:",step1)
#中间结果再乘以mu=1时的余弦信号,得到最终结果
step2=step1@A[1,:]
print("result of step2:",step2)
二维DCT正变换的矩阵计算
在上面的两个例子中,我分别列举了频域中两个点的计算,事实上,和一维DCT一样,二维信号所有点的DCT可以通过矩阵运算的方式一次性求出来,具体来说就是先对二维信号的每一行进行一维DCT,得到一个中间结果矩阵。然后对中间结果的每一列进行一维DCT,得到最终的二维DCT结果。
1,首先,一维DCT的矩阵计算公式为:
2, 基于这一公式,我们可以先用实现对x的每行数据进行一维DCT变换。其中,可拆分成下图的计算方式,其中每个Acol是矩阵中的一列。
上面公式中的每一列又是按照下图的方式计算的:
上图的意思是矩阵A的每一行都是一组cos基础函数,每行cos函数乘以col中的数据就是col的DCT系数。又因为col中所装的都来自于,即x的转置。因此,矩阵A所乘的每一列col就是原始二维数据中的每一行。A乘col的结果是一个列向量。
3,上一步中的结果是一个与x尺寸相同的矩阵,是中间结果。该矩阵中的每一列都是原始二维矩阵x每行一维DCT的结果。首先,我们要对中间结果转置,即,,转置后矩阵中每一行都是x每一行DCT的结果。然后再对转置后的中间结果的每一列做一维DCT,直接套用一维DCT的矩阵计算公式即可,得到:
最终得到二维DCT的矩阵运算公式:
用python代码实现如下:
X2=(A@x2)@A.T
X2
这一计算结果与之前用cv2库计算所得的结果一致!
二维DCT反变换
已知二维DCT正变换的矩阵计算公式为:
现在要求二维DCT反变换,即已知X,要求x,需要分两步计算:
1,等式两边的前面同时乘以A^T,得到:
2,等式两边的后面同时乘以A:
最终得到二维DCT反变换的公式:
print("Original data:\n",x2)
print("\nForward 2d DCT:\n",X2)
x2_inv=A.T@X2@A
print("\nInv 2d DCT:\n",x2_inv)
用相应的库函数验证DCT结果:
idct_matrix = idct(idct(dct_matrix.T, norm='ortho').T, norm='ortho')
idct_matrix
(全文完)
作者 --- 松下J27
参考文献:
1,《数字图像处理技术详解与Visual C++实践》---左飞
3,dct
4,https://en.wikipedia.org/wiki/Discrete_cosine_transform
5,https://en.wikipedia.org/wiki/Cross-correlation
6,Amplitude, Period, Phase Shift and Frequency
(配图与本文无关)
版权声明:文中的部分图片,文字或者其他素材,可能来自很多不同的网站和说明,在此没法一一列出,如有侵权,请告知,立即删除。欢迎大家转载,但是,如果有人引用或者COPY我的文章,必须在你的文章中注明你所使用的图片或者文字来自于我的文章,否则,侵权必究。 ----松下J27