离散余弦变换(Discrete Cosine Transform,DCT):是一种数学变换,它将一连串的离散值,如数字图像或信号,转换为代表图像或信号的频率成分的系数序列。DCT被广泛用于图像和信号处理、数据压缩和数字通信中。DCT的主要优点之一是它提供了一个图像或信号在其主要频率成分方面的紧凑表示,使其有可能在不损失很多信息的情况下压缩数据。DCT类似于更知名的傅里叶变换,但更适合于处理现实世界中具有有限长度和非周期性的信号和图像。DCT被用于几种流行的图像和视频压缩格式,如JPEG和MPEG
一维DCT变换定义:一维DCT是一种数学变换,它为一个给定的输入序列计算一组频率系数,使用不同频率的余弦函数的加权和。由此产生的频率系数可用于分析和压缩输入序列,以及其他应用。定义式如下
F ( u ) = C ( u ) 2 N ∑ x = 0 N − 1 f ( x ) cos ( 2 x + 1 ) u π 2 N u = 0 , 1 , ⋯ , N − 1 F(u)=C(u) \sqrt{\frac{2}{N}} \sum_{x=0}^{N-1} f(x) \cos \frac{(2 x+1) u \pi}{2 N} \quad u=0,1, \cdots, N-1 F(u)=C(u)N2x=0∑N−1f(x)cos2N(2x+1)uπu=0,1,⋯,N−1
一维IDCT变换定义:IDCT是DCT的逆变换,定义式如下
f ( x ) = 2 N ∑ u = 0 N − 1 C ( u ) F ( u ) cos ( 2 x + 1 ) u π 2 N x = 0 , 1 , ⋯ , N − 1 f(x)=\sqrt{\frac{2}{N}} \sum_{u=0}^{N-1} C(u) F(u) \cos \frac{(2 x+1) u \pi}{2 N} \quad x=0,1, \cdots, N-1 f(x)=N2u=0∑N−1C(u)F(u)cos2N(2x+1)uπx=0,1,⋯,N−1
其中 C ( u ) = { 1 2 u = 0 1 u = 1 , 2 , … , N − 1 C(u)=\left\{\begin{array}{lc}\frac{1}{\sqrt{2}} & u=0 \\1 & u=1,2, \ldots, N-1\end{array}\right. C(u)={211u=0u=1,2,…,N−1
如下,有一长为4的数组序列,求其DFT
F ( u ) = C ( u ) 2 4 ∑ x = 0 3 f ( x ) cos ( 2 x + 1 ) u π 8 u = 0 , 1 , 2 , 3 F(u)=C(u) \sqrt{\frac{2}{4}} \sum_{x=0}^{3} f(x) \cos \frac{(2 x+1) u \pi}{8} \quad u=0,1,2,3 F(u)=C(u)42x=0∑3f(x)cos8(2x+1)uπu=0,1,2,3
矩阵形式如下
二维DCT变换与逆变换定义:二维离散余弦变换(DCT)是一维DCT向二维的扩展。它用于将二维信号或图像转换为二维频域中的一组频率系数。总之,二维DCT是一种数学变换,它为给定的输入图像计算一组频率系数,使用具有不同水平和垂直频率的余弦函数的加权和。由此产生的频率系数可用于分析和压缩输入图像,以及其他应用
其中
x , u = 0 , 1 , 2 , ⋯ , M − 1 y , v = 0 , 1 , 2 , ⋯ , N − 1 C ( u ) , C ( v ) = { 1 2 u , v = 0 1 u , v = 1 , 2 , … , N − 1 \begin{array}{c}x, u=0,1,2, \cdots, M-1 \\y, v=0,1,2, \cdots, N-1 \\C(u), C(v)=\left\{\begin{array}{ll}\frac{1}{\sqrt{2}} & u, v=0 \\1 & u, v=1,2, \ldots, N-1\end{array}\right.\end{array} x,u=0,1,2,⋯,M−1y,v=0,1,2,⋯,N−1C(u),C(v)={211u,v=0u,v=1,2,…,N−1
二维DCT变换的矩阵形式表示为
F = A f A T , f = A T F A F=AfA^{T},f=A^{T}FA F=AfAT,f=ATFA
如下有一幅输入图像 f ( x , y ) f(x,y) f(x,y),使用矩阵算法求其DCT
如下
MATLAB实现:相关函数如下,具体解释可看MATLAB帮助手册
dct
:这个函数计算给定输入信号或序列的一维离散余弦变换(DCT)。DCT用于将输入信号转换为频域中的一组频率系数。DCT函数需要以下参数
Src
:输入信号或序列dst
:输出频率系数flags
:一组控制DCT行为的标志nonzeroRows
:输入信号中非零行的数量(可选)idct
:该函数计算一组给定频率系数的一维离散余弦逆变换(IDCT)。IDCT用于将频率系数转换回原始信号或序列。idct函数接收以下参数
src
:输入频率系数dst
:输出信号或序列flags
:一组控制IDCT行为的标志nonzeroRows
:输入频率系数中非零行的数量(可选)dct2
:该函数计算给定输入图像的二维离散余弦变换(DCT)。DCT用于将输入图像转换为二维频域中的一组频率系数。dct2函数接受以下参数
src
:输入图像dst
:输出频率系数flags
:一组控制DCT行为的标志nonzeroRows
:输入图像中非零行的数量(可选)nonzeroCols
:输入图像中非零列的数量(可选)idct2
:这个函数计算一组给定频率系数的二维离散余弦逆变换(IDCT)。IDCT用于将频率系数转换回原始图像。idct2函数接受以下参数
src
:输入频率系数dst
:输出图像flags
:一组控制IDCT行为的标志nonzeroRows
:输入频率系数中非零行的数量(可选)nonzeroCols
:输入频率系数中非零列的数量(可选)实现如下效果
Image=imread('cameraman.jpg');%读取图像
imshow(Image);%显示原图像
grayI=rgb2gray(Image);%将彩色图像灰度化
figure,imshow(grayI);
DCTI=dct2(grayI);%计算余弦变换并移位
ADCTI=abs(DCTI);
top=max(ADCTI(:));
bottom=min(ADCTI(:));
ADCTI=(ADCTI-bottom)/(top-bottom)*100;
figure,imshow(ADCTI);%显示余弦变换频谱图
imwrite(ADCTI,'cameramandct.jpg');
Python实现:使用Python实现上述同样的功能
cv2.dct
:DCTcv2.idct
:IDCTimport cv2
import numpy as np
from matplotlib import pyplot as plt
# 读取图像
Image = cv2.imread('cameraman.jpg')
# 显示原图像
cv2.imshow('Original Image', Image)
cv2.waitKey(0)
# 将彩色图像灰度化
grayI = cv2.cvtColor(Image, cv2.COLOR_BGR2GRAY)
# 显示灰度图像
cv2.imshow('Grayscale Image', grayI)
cv2.waitKey(0)
# 计算离散余弦变换
DCTI = cv2.dct(np.float32(grayI))
# 计算幅值并将其归一化到0-100之间
ADCTI = cv2.convertScaleAbs(DCTI)
ADCTI = cv2.normalize(ADCTI, None, 0, 100, cv2.NORM_MINMAX)
# 显示余弦变换频谱图
plt.imshow(ADCTI, cmap='gray')
plt.show()
# 保存离散余弦变换频谱图
cv2.imwrite('cameramandct.jpg', ADCTI)
DCT在图像处理中的应用:
K-L变换(Karhunen-Loeve Transform):是建立在统计特性基础上的一种变换,又称为霍特林(Hotelling)变换或主成分分析(PCA)。K-L变换的突出优点是相关性好,是均方误差(MSE,Mean Square Error)意义下的最佳变换,它在数据压缩技术中占有重要地位
注意:K-L变换和主成分分析关系虽然很密切,但不能说是一回事
PCA与K-L变换密切相关,因为这两种技术都涉及寻找协方差矩阵的特征向量和特征值。事实上,从PCA得到的主成分是协方差矩阵的特征向量,而特征值代表每个主成分所捕获的方差量。综上所述,虽然K-L变换和PCA有一些相似之处,但它们并不完全是一回事。K-L变换是一种用于信号处理的数学技术,而PCA是一种用于降维和特征提取的统计技术。
假设有一个二维随机向量 x = [ x 1 , x 2 ] \mathbf{x} = [x_1, x_2] x=[x1,x2],其均值为 μ = [ μ 1 , μ 2 ] \boldsymbol{\mu} = [\mu_1, \mu_2] μ=[μ1,μ2],协方差矩阵为 C x \mathbf{C}_{\mathbf{x}} Cx。K-L 展开的目的是找到一组正交基函数 ϕ i ( x ) {\phi_i(\mathbf{x})} ϕi(x),使得随机向量 x \mathbf{x} x 可以被表示为
x = μ + ∑ i = 1 ∞ z i λ i ϕ i ( x ) \mathbf{x}=\boldsymbol{\mu}+\sum_{i=1}^{\infty} z_{i} \sqrt{\lambda_{i}} \boldsymbol{\phi}_{i}(\mathbf{x}) x=μ+i=1∑∞ziλiϕi(x)
其中, z i {z_i} zi 是一组独立同分布的随机变量, ϕ i ( x ) {\boldsymbol{\phi}_i(\mathbf{x})} ϕi(x) 是一组标准正交函数, λ i \lambda_i λi 是 C x \mathbf{C}_{\mathbf{x}} Cx 的第 i i i 个特征值, ϕ i ( x ) \phi_i(\mathbf{x}) ϕi(x) 是 C x \mathbf{C}_{\mathbf{x}} Cx 的第 i i i 个特征向量
由于特征向量 ϕ i ( x ) {\boldsymbol{\phi}_i(\mathbf{x})} ϕi(x) 是标准正交函数,因此 K-L 展开保证了展开系数 z i z_i zi 是独立的。此外,K-L 展开中的基函数 ϕ i ( x ) {\boldsymbol{\phi}_i(\mathbf{x})} ϕi(x) 是协方差矩阵 C x \mathbf{C}_{\mathbf{x}} Cx 的特征向量,因此它们对应的是数据中的主成分,可以用于数据降维和特征提取等任务
K-L 展开的实现可以通过对协方差矩阵进行特征值分解得到。在实际应用中,由于数据维度往往很高,因此需要使用一些数值技巧来加速计算,例如矩阵的迹技巧和 Lanczos 迭代等
离散K-L变换(Discrete Karhunen-Loève Transform,DKLT):是 K-L 变换在离散信号处理中的一种形式。它将离散信号表示为一组正交基函数的线性组合,类似于离散余弦变换(DCT)和离散小波变换(DWT)
设有一个 N N N 维离散信号 x = [ x 1 , x 2 , ⋯ , x N ] \mathbf{x} = [x_1, x_2, \cdots, x_N] x=[x1,x2,⋯,xN],其均值为 μ = 1 N ∑ i = 1 N x i \mu = \frac{1}{N}\sum_{i=1}^{N}x_i μ=N1∑i=1Nxi,协方差矩阵为 C x \mathbf{C}_\mathbf{x} Cx。DKLT 的目的是找到一组正交基函数 ϕ i ( x ) {\boldsymbol{\phi}_i(\mathbf{x})} ϕi(x),使得信号 x \mathbf{x} x 可以被表示为
x = μ + ∑ i = 1 ∞ z i λ i ϕ i ( x ) \mathbf{x}=\boldsymbol{\mu}+\sum_{i=1}^{\infty} z_{i} \sqrt{\lambda_{i}} \boldsymbol{\phi}_{i}(\mathbf{x}) x=μ+i=1∑∞ziλiϕi(x)
其中, z i {z_i} zi 是一组独立同分布的随机变量, ϕ i ( x ) \boldsymbol{\phi}_i(\mathbf{x}) ϕi(x) 是信号 x \mathbf{x} x 的第 i i i 个正交基函数, λ i \lambda_i λi 是 C x \mathbf{C}_{\mathbf{x}} Cx 的第 i i i 个特征值
在实际应用中,由于离散信号的维度往往很高,因此需要使用数值方法来计算 DKLT。一种常用的方法是使用矩阵特征值分解。设 X \mathbf{X} X 是将信号 x \mathbf{x} x 按列排成的 N × N N\times N N×N 矩阵, Λ \mathbf{\Lambda} Λ 是协方差矩阵 C x \mathbf{C}_{\mathbf{x}} Cx 的特征值对角矩阵, U \mathbf{U} U 是 C x \mathbf{C}_{\mathbf{x}} Cx 的特征向量组成的矩阵,则 DKLT 的系数 z i {z_i} zi 可以通过以下公式计算
z = U T ( X − μ ) \mathbf{z}=\mathbf{U}^{T}(\mathbf{X}-\boldsymbol{\mu}) z=UT(X−μ)
其中, μ \boldsymbol{\mu} μ 是信号 x \mathbf{x} x 的均值向量。然后,可以通过以下公式将信号 x \mathbf{x} x 进行还原
x = μ + U z \mathbf{x}=\mathbf{\mu}+\mathbf{Uz} x=μ+Uz
与其他离散变换类似,DKLT 可以用于信号压缩、数据降维和特征提取等领域
clc,clear;
X=[0 0 0;1 0 1;1 0 0;1 1 0;0 0 1;0 1 1;0 1 0;1 1 1]';
[n, N]=size(X);
V=X*X'/N;
[coeff, D]=eigs(V);
[D_sort,index] = sort(diag(D),'descend');
D=D(index,index);
coeff = coeff(:,index);
score=coeff'*X;
figure; plot(score(1,:),score(2,:),'ko'),title('K-L变换');
xlabel('第一主成分得分');ylabel('第二主成分得分');
图像K-L变换:是一种将图像转换到一个新的基下的线性变换,旨在提取图像的主要特征。其数学表达式与离散 K-L 变换类似,但是在图像处理中需要将信号扩展到两维
设有一幅 M × N M \times N M×N 的灰度图像 I \mathbf{I} I,其均值为 μ \mu μ,协方差矩阵为 C I \mathbf{C_I} CI。图像 K-L 变换的目的是找到一组正交基函数 ϕ i , j ( I ) {\boldsymbol{\phi}_{i,j}(\mathbf{I})} ϕi,j(I),使得图像 I \mathbf{I} I 可以被表示为
I = μ + ∑ i = 1 M ∑ j = 1 N z i , j λ i , j ϕ i , j ( I ) \mathbf{I}=\mu+\sum_{i=1}^{M} \sum_{j=1}^{N} z_{i, j} \sqrt{\lambda_{i, j}} \phi_{i, j}(\mathbf{I}) I=μ+i=1∑Mj=1∑Nzi,jλi,jϕi,j(I)
其中, z i , j {z_{i,j}} zi,j 是一组独立同分布的随机变量, ϕ i , j ( I ) \boldsymbol{\phi}_{i,j}(\mathbf{I}) ϕi,j(I) 是图像 I \mathbf{I} I 的第 ( i , j ) (i,j) (i,j) 个正交基函数, λ i , j \lambda_{i,j} λi,j 是 C I \mathbf{C_I} CI 的第 ( i , j ) (i,j) (i,j) 个特征值
在实际应用中,通常采用二维离散余弦变换(2D-DCT)作为正交基函数。具体来说,对于一幅 M × N M \times N M×N 的图像 I \mathbf{I} I,可以将其分解为 M M M 个行向量和 N N N 个列向量,然后对每一行和每一列分别进行一维离散余弦变换。设变换后的结果为 F \mathbf{F} F,则 F \mathbf{F} F 的第 ( i , j ) (i,j) (i,j) 个元素为
F i , j = 2 M N cos [ ( 2 i − 1 ) ( j − 1 ) π 2 M ] cos [ ( 2 j − 1 ) ( i − 1 ) π 2 N ] ∑ m = 1 M ∑ n = 1 N ( I m , n − μ ) cos [ ( 2 m − 1 ) ( i − 1 ) π 2 M ] cos [ ( 2 n − 1 ) ( j − 1 ) π 2 N ] \begin{array}{l}F_{i, j}=\sqrt{\frac{2}{M N}} \cos \left[\frac{(2 i-1)(j-1) \pi}{2 M}\right] \cos \left[\frac{(2 j-1)(i-1) \pi}{2 N}\right] \sum_{m=1}^{M} \sum_{n=1}^{N}\left(I_{m, n}-\right. \mu) \cos \left[\frac{(2 m-1)(i-1) \pi}{2 M}\right] \cos \left[\frac{(2 n-1)(j-1) \pi}{2 N}\right]\end{array} Fi,j=MN2cos[2M(2i−1)(j−1)π]cos[2N(2j−1)(i−1)π]∑m=1M∑n=1N(Im,n−μ)cos[2M(2m−1)(i−1)π]cos[2N(2n−1)(j−1)π]
其中, I m , n I_{m,n} Im,n 是原始图像 I \mathbf{I} I 的第 ( m , n ) (m,n) (m,n) 个像素值。由于 DKLT 是一种正交变换,因此它可以保持图像的信息不变,但是将其表示为更加紧凑的形式。在实际应用中,图像 K-L 变换可以用于图像压缩、特征提取、图像分类等领域
MATLAB实现:相关函数如下,具体解释可看MATLAB帮助手册
clear,clc,close all;
fmt={'*.jpg','JPEG image(*.jpg)';'*.*','All Files(*.*)'};
[FileName,FilePath]=uigetfile(fmt,'导入数据','face*.jpg','MultiSelect','on');
if ~isequal([FileName,FilePath],[0,0])
FileFullName=strcat(FilePath,FileName);
else
return;
end
N=length(FileFullName);
for k=1:N
Image=im2double(rgb2gray(imread(FileFullName{k})));
X(:,k) = Image(:); % 把图像放在矩阵x的第k列
end
[h,w,c]=size(Image);
% %----------- 计算每幅训练图像的与平均脸的差值 -------%
averagex = mean(X')'; %计算均值图像
X=X-averagex; % 求中心化图像向量
%-----奇异值分解方法计算协方差矩阵的特征值和特征向量----%
R = X'*X; %协方差矩阵为x*x’,这里用奇异值分解
[Q,D] = eig(R); %V为以特征向量为列的矩阵,D为特征值组成的对角阵
[D_sort,index] = sort(diag(D),'descend');
D=D(index,index);
Q = Q(:,index);
P = X*Q*(abs(D))^-0.5;
total = 0.0;
count = sum(D_sort);
for r =1:N
total = total + D_sort(r);
if total/count > 0.95 %当差异信息比例达到85%时退出循环
break;
end
end
%-------------测试样本在新空间上投影后的坐标-----------%
KLCoefR = P'*X;
figure; plot(KLCoefR(1,:),KLCoefR(2,:),'ko'),title('K-L变换行压缩');
xlabel('第一主成分得分');ylabel('第二主成分得分');
Y= P(:,1:2)*KLCoefR(1:2,:)+averagex; %重建
for j=1:N
outImage=reshape(Y(:,j),h,w);
% top=max(outImage(:));
% bottom=min(outImage(:));
% outImage=(outImage-bottom)/(top-bottom);
% str=strcat('feaface12_',num2str(j),'.jpg');
% imwrite(outImage,str);
figure,imshow(outImage,[]);
end
Z= P(:,1:r)*KLCoefR(1:r,:)+averagex; %重建
for j=1:N
outImage=reshape(Z(:,j),h,w);
% top=max(outImage(:));
% bottom=min(outImage(:));
% outImage=(outImage-bottom)/(top-bottom);
% str=strcat('feaface1r_',num2str(j),'.jpg');
% imwrite(outImage,str);
figure,imshow(outImage,[]);
end
KLCoefC = X*Q;
for j =1:N
outImage=reshape(KLCoefC(:,j),h,w);
% top=max(outImage(:));
% bottom=min(outImage(:));
% outImage=(outImage-bottom)/(top-bottom);
% str=strcat('feaface',num2str(j),'.jpg');
% imwrite(outImage,str);
figure,imshow(outImage,[]);
end
%
%
Python实现:使用Python实现上述同样的功能
import cv2
import numpy as np
import matplotlib.pyplot as plt
# 导入数据
fmt = [("*.jpg", "JPEG image(*.jpg)"), ("*.*", "All Files(*.*)")]
FileName, FilePath = tkinter.filedialog.askopenfilename(filetypes=fmt, title="导入数据", initialdir=".", multiple=True)
if FileName:
N = len(FileName)
X = np.zeros((h * w, N))
for k in range(N):
Image = cv2.imread(FileName[k])
Image = cv2.cvtColor(Image, cv2.COLOR_BGR2GRAY).astype(np.float64)
X[:, k] = np.reshape(Image, -1) # 把图像放在矩阵x的第k列
[h, w] = Image.shape
# 计算每幅训练图像的与平均脸的差值
averagex = np.mean(X, axis=1)
X = X - np.tile(averagex[:, np.newaxis], (1, N))
# 奇异值分解方法计算协方差矩阵的特征值和特征向量
R = np.dot(X.T, X)
[D, Q] = np.linalg.eigh(R) # Q为以特征向量为列的矩阵,D为特征值组成的对角阵
idx = D.argsort()[::-1]
D = D[idx]
Q = Q[:, idx]
P = np.dot(X, Q)
P = np.dot(P, np.diag(1 / np.sqrt(abs(D) + np.finfo(np.float32).eps))) # 使P正交
total = 0.0
count = np.sum(D)
for r in range(N):
total += D[r]
if total / count > 0.95:
break
# 测试样本在新空间上投影后的坐标
KLCoefR = np.dot(P.T, X)
plt.plot(KLCoefR[0, :], KLCoefR[1, :], "ko")
plt.title("K-L变换行压缩")
plt.xlabel("第一主成分得分")
plt.ylabel("第二主成分得分")
plt.show()
Y = np.dot(P[:, :2], KLCoefR[:2, :]) + np.tile(averagex[:, np.newaxis], (1, N)) # 重建
for j in range(N):
outImage = np.reshape(Y[:, j], (h, w))
plt.imshow(outImage, cmap="gray")
plt.show()
Z = np.dot(P[:, :r], KLCoefR[:r, :]) + np.tile(averagex[:, np.newaxis], (1, N)) # 重建
for j in range(N):
outImage = np.reshape(Z[:, j], (h, w))
plt.imshow(outImage, cmap="gray")
plt.show()
KLCoefC = np.dot(X, Q)
for j in range(N):
outImage = np.reshape(KLCoefC[:, j], (h, w))
plt.imshow(outImage, cmap="gray")
plt.show()