DCT变换

灰度图测试

由于DCT只能对单通道数据进行,所以先介绍灰度图。

图像有RGB表示和**YCbCr(亮度、色度Chroma)**表示等。

利用matlab的rgb2gray函数可以将3通道彩色图转换为灰度图

得到图像灰度图还有另一种方式,就是用matlab的rgb2ycbcr函数转换为YCbCr颜色表示,然后单独将Y通道提取出来显示就是灰度图。

经过试验,得到的图像视觉上没有明显差异,但灰度图数值不同

%% 读取图像
clc;clear;close all;
img = imread("image\lena.png");
img_gray = rgb2gray(img);
img_ycbcr = rgb2ycbcr(img);
img_y = img_ycbcr(:,:,1);

%% 看是否灰度图和YCbCr的Y分量相不相同
subplot(1,2,1);
imshow(img_gray);
subplot(1,2,2);
imshow(img_ycbcr(:,:,1)); % 视觉效果没有明显差异,但是数值不一样

image-20240615135221579

image-20240615135318675

图像的高频与低频

图像不仅可以看做是像素点的组合,还可以从信号的角度来看,我们可以将图像的任意一行取出来绘制折线图:

%% 画256行的一个折线图
img_row = img_gray(256,:);
figure;
plot(img_row);

%% 在三维空间绘制像素变化情况
[height, width] = size(img_gray);
[X, Y] = meshgrid(1:width, 1:height);
Z = double(img_gray);
figure;
surf(X, Y, Z);
colormap('jet');
colorbar;
view(3);
axis equal;
grid on;
shading interp;
xlabel('X');
ylabel('Y');
zlabel('Pixel Value');
title('Image as a Smooth Surface');

image-20240615135701799

image-20240616143319742

image-20240615140720439

可以看到整个像素行有变化剧烈的部分以及变化平缓的部分。

实际上图像的低频信号就是像素变化平缓的部分,而像素的高频信号就是像素变化剧烈的部分,也常常有高频噪声的说法。而人眼对于高频细节信号不敏感的。对于低频信号较为敏感。

需要注意的一点是,现实世界中由相机拍摄的图像一般在像素间由较强的相关性,也就是一个小区域内的像素值一般比较接近,所以应该由大量低频信号组成,而高频信号只占一小部分

那么图像的高频低频,甚至于是不同频率的信号如何进行提取,这就需要用到DCT变换(Discrete Cosine Transform),通过DCT变换逆DCT变换,可以将一幅图像的像素值用不同频率余弦分量的线性组合表示。

DCT变换现象观察

理论上,DCT变换可以应用于任意长度的输入,但简单起见,只考虑8个样本点:

image-20240615143421253

考虑8个样本点 x 0 , x 1 , . . x 7 x_0,x_1,..x_7 x0,x1,..x7,经过DCT变换后得到一个等长的系数向量 X 0 , X 1 , . . . , X 7 X_0,X_1,...,X_7 X0,X1,...,X7,这就是离散余弦变换的目的:得到原始信号中由哪些余弦波组成,以及组成系数是多少。这里的 C 0 , C 1 , . . . C 7 C_0,C_1,...C_7 C0,C1,...C7就是不同频率的余弦波。

为了直观地看到当原始信号变化时,得到的DCT变换系数如何变化,我们不妨考虑输入一个标准余弦函数:

image-20240615143914754

由于余弦函数是连续的,要应用DCT,我们首先需要对其进行采样,也就是在 [ 0 , π ] [0,\pi] [0,π]内采样8个样本点,这里用如下的采样方法:将 [ 0 , π ] [0,\pi] [0,π]区间分成8等份,然后以每个子区间的中点位置为一个样本点(实际上这样采样和DCT变换中的余弦波保持 了一致):

image-20240615144444389

现在只考虑 N = 8 N=8 N=8的情况,其他情况可以类似地推广。

现在我们可以将采样得到的样本点向量输入DCT变换了,我们将观察到原始信号在以下三种情况下,DCT系数的变化情况:

  • 振幅变化
  • 上下平移
  • 频率变化
振幅变化
%% 振幅变化时 DCT的变化情况:DCT对应分量也变化,如果反向,DCT图像也会跟着反向
figure;
for k = [-1,1,2]
    for i = 0:7
        y(i+1) = k * cos((2*i+1)*pi / 16); %对标准余弦函数cos(x)进行采样,[0,π]采样8个点
    end
    y_dct = dct(y);
    plot(y_dct);
    hold on;
end
title("余弦函数振幅变化时,其DCT的变化情况")
legend("取负","振幅为1","振幅为2");

image-20240615144945435

可以看到,当振幅变化时,DCT系数对应的第2个分量 X 1 X_1 X1的值也发生了变化,而其他分量都是0

上下平移
%% 上下平移时,DCT的变化情况
figure;
for b = [-1,1]
    for i = 0:7
        y(i+1) = cos((2*i+1)*pi / 16) + b;
    end
    y_dct = dct(y);
    plot(y_dct);
    hold on;
end
title("余弦函数上下平移时,其DCT的变化情况")
legend("向下平移1","向上平移1");

image-20240615145415900

当原始余弦信号上下平移时,可以看到第二个DCT系数峰值依然存在并保持不变,而上下平移只改变了第一个DCT系数 X 0 X_0 X0的值(后面将得到第一个系数实际上直流分量,其他系数是交流分量),除前两个系数外,其他DCT系数都是0

频率变化
%% 频率变化时,DCT的变化情况
figure;
for k = 1:3
    for i = 0:7
        y(i+1) = cos((2*i+1)*pi*k / 16);
    end
    y_dct = dct(y);
    subplot(1,3,k);
    plot(y_dct);
    title(strcat("频率系数为", num2str(k)));
end
sgtitle("余弦函数频率变化时,DCT的变化情况");

image-20240615145808908

当原始余弦信号频率发生变化时,可以看到DCT系数峰值的位置也发生了变化,频率与其峰值的 x x x​索引一一对应的。可以以此类推,频率系数为4时,峰值对应的是 X 4 X_4 X4

我们发现频率系数1-7的的余弦波都有相对应的峰值索引,那剩余的频率系数为0的余弦波,由之前的上下平移变换可以得知,它对应的是索引为0的DCT系数。实际上频率为0的余弦波其实就是常值信号, c o s ( 0 ) = 1 cos(0)=1 cos(0)=1​。如果放到图像上来看,它其实就是一组图像的整体亮度,一组像素的整体亮度越高,那么它的索引为0的DCT系数就越大。反之越低。

实际上,每个频率都对应着一种图像模式,DCT所做的就是分解出各个基本模式,并计算它们对于原始图像的贡献

image-20240615151444438

下面我们将通过数学公式看到,8个像素值的所有可能组合都可以表示为这8个余弦波的总和

DCT数学原理

正变换

DCT系数的公式如下:

image-20240615152111184

我们可以发现公式中的余弦函数和最开始我们定义的余弦函数采样点相同,那么DCT各个分量系数实际上就表示为原始信号的采样点余弦波的样本点

为什么要做积呢,我们将公式换一种写法很容易就可以理解:

image-20240615152724223

实际上就是余弦信号采样点与原始信号向量做了点积,而我们知道点积是衡量向量相关性的一个重要方法。这就是为什么我们之前对频率系数为 k k k的余弦信号采样,做DCT变换后,其峰值 X k X_k Xk处,因为两个向量是完全相同的,所以其点积也是最大的。

更一般地,我们可以将DCT变换写成余弦分量矩阵原始信号向量乘积的形式:

image-20240615153903067

这个计算实质上就是一个简单的对原始信号的线性变换,但余弦信号分量矩阵具有重要的性质,那就是两两不同行相互正交,这也解释了为什么在前面的实验中,当我们向DCT输入一个特定频率的余弦信号时,只有一个峰值,而其他的系数都是0

逆变换

DCT变换的一个重要性质就是它是可逆的,由DCT系数我们可以无损地还原出原始信号。由正变换的矩阵形式,我们很容易就可以得出逆变换是怎样的:

image-20240615154419212

很重要的一点是,由于余弦分量矩阵不同行之间两两正交,所以它是一个近正交矩阵( A A T = k E AA^T=kE AAT=kE​),正交矩阵的逆就等于其转置,由于一个相同行相乘不一定等于1,所以在求逆时我们只需要用其转置再乘以一个归一化系数即可。

DCT逆变换实质上就是对余弦波进行线性组合,由于8个余弦波是两两正交的,就相当于一组基,所以任意8个原始信号采样都可以用这8个余弦波的线性组合表示。

实际应用

实际应用的时候,DCT变换采用如下公式:
X ( k ) = 2 / N ⋅ c ( u ) ⋅ ∑ n = 0 N − 1 x ( n ) c o s [ k ( 2 n + 1 ) π 2 N ] , k = 0 , 1 , . . . , N − 1 c ( u ) = { 1 2 u = 0 1 u > 0 X(k)=\sqrt{2/N}·c(u)·\sum_{n=0}^{N-1}x(n)cos[\frac {k(2n+1)\pi} {2N}],k=0,1,...,N-1 \\ c(u) = \begin{cases} \frac 1 {\sqrt{2}} \hspace{5mm}&u=0\\ 1 & u>0 \end{cases} X(k)=2/N c(u)n=0N1x(n)cos[2Nk(2n+1)π],k=0,1,...,N1c(u)={2 11u=0u>0
公式前部的 2 / N ⋅ c ( u ) \sqrt{2/N}·c(u) 2/N c(u)是归一化系数,保证了余弦分量矩阵是正交的。可以进行测试:

%% DCT归一化系数 N=8
y(1,1:8) = sqrt(1/8);
for i = 1:7
    for j = 0:7
        y(i+1,j+1) = sqrt(2/8) * cos((2*j+1)*pi*i / 16);
    end
end
s = y*y'; %s=单位矩阵E

image-20240615172800948

这里再介绍一个matlab中dctmtx的函数,该函数就是求DCT变换中归一化余弦分量矩阵的函数:

%% dctmtx函数测试
y(1,1:8) = sqrt(1/8);
for i = 1:7
    for j = 0:7
        y(i+1,j+1) = sqrt(2/8) * cos((2*j+1)*pi*i / 16);
    end
end
c = dctmtx(8); % 传入参数是N,即采样点数量

image-20240615174905652

可以看到用dctmtx函数计算出的余弦分量矩阵与我们手动计算的完全相同。

二维DCT变换

前面介绍的一维DCT变换,那要应用于图像需要二维DCT变换,变换过程就是先对图像的每一行进行一维DCT变换得到一个DCT系数矩阵,再将这个DCT系数矩阵的每一列进行一维的DCT变换得到最终的二维DCT变换系数。(DCT变换的可分离性:也可以先列后行,经过数学证明结果是一样的)

image-20240615155445969

DCT变换还具有能量集中的性质,意思是DCT系数矩阵左上角的DCT系数通常较大,这是因为一般图像中大部分都是低频信号,高频信号(右下)只占一小部分,这也是为什么在JPEG压缩中,右下角都可以量化成0的原因,图像由一些低频信号就已经能够构建出大致的模样,这也是人眼最敏感的。

数学公式

img

由于DCT变换具有可分离的性质,所以可以将二维DCT拆分成两个方向的一维DCT:

img

写成矩阵形式就是:

image-20240615173821603

image-20240615183710773

image-20240615183759855

由于矩阵乘法具有结合律,所以 Y = ( C X ) C T = C ( X C T ) Y=(CX)C^T=C(XC^T) Y=(CX)CT=C(XCT),表明无论是先对行做DCT再对列做DCT还是先对列做DCT再对行做DCT结果都一样。

基图像

上面公式中的 B i , j B_{i,j} Bi,j即为基图像, B i , j = β i β j T B_{i,j}=\beta_i \beta_j^T Bi,j=βiβjT,维度是 N × N N×N N×N,共 N × N N×N N×N​种频率的基图像,使用基图像求DCT系数进行两次一维DCT变换求DCT系数等价。

利用基图像进行2D DCT就是把对应频率的基图像和原始图像信号做逐像素乘积再对矩阵整体进行求和即可得到相应频率的DCT系数。

%% 二维DCT基图像
A = dctmtx(8);
B = A';
C = zeros(8, 8, 64);
m = 0;
for i = 1:8
    for j = 1:8
        m = m+1;
        C(:, :, m) = B(:, i)*A(j, :);
    end
end
minvalue = min(min(min(C)));
maxvalue = max(max(max(C)));
figure,
for k = 1:64
    subplot(8, 8, k)
    imshow(C(:, :, k), [minvalue, maxvalue]);
end

image-20240615183011355

DCT在图像变换时的性质(可用于设计抗相似性变换的水印)

索引从0开始,有以下性质:

image-20241105201711443

参考链接

[1] https://ww2.mathworks.cn/help/images/discrete-cosine-transform.html

[2] https://www.bilibili.com/video/BV1bc411q7YG/?spm_id_from=333.337.search-card.all.click&vd_source=bac8ddf04ec0b6386d58110f67353bc7

[3] https://blog.csdn.net/BigDream123/article/details/101426393

[4] https://blog.csdn.net/m0_51143578/article/details/129344131

[5] Guan H, Zeng Z, Liu J, et al. A novel robust digital image watermarking algorithm based on two-level DCT[C]//2014 International Conference on Information Science, Electronics and Electrical Engineering. IEEE, 2014, 3: 1804-1809.

### DCT变换的实现原理 离散余弦变换(Discrete Cosine Transform, DCT)是一种用于信号处理和数据压缩的重要工具。它通过将空间域中的数据映射到频率域,从而能够有效地表示图像或其他信号的主要特征[^1]。 #### 间接算法 一种常见的DCT实现方式是间接算法,该方法依赖于与其他正交变换的关系,例如离散傅里叶变换(DFT)。具体来说,这种方法会先将输入数据转化为适合DFT的形式,再利用快速傅里叶变换(FFT)完成计算。然而,这种间接方法通常需要额外的操作步骤来调整结果以适应DCT的要求。尽管其实现相对简单,但由于效率低下以及适用范围有限,在现代应用中较少使用[^1]。 #### 直接算法 更高效的DCT实现通常是基于直接算法。这类算法主要包括矩阵分解技术和递归算法两种形式: - **矩阵分解**:此技术的核心思想是对原始的DCT变换矩阵进行稀疏化分解,将其拆解成若干简单的子操作序列。这些子操作可以进一步优化硬件或软件上的执行速度。 - **递归算法**:相比矩阵分解,递归算法则采取了一种自底向上的构建策略——即从小规模的DCT出发逐步扩展至更大尺寸的情况。这种方式不仅保持了较高的数值精度,还具备更好的可移植性和灵活性[^1]。 ### Verilog编程实现DCT 对于嵌入式系统或者专用集成电路设计而言,Verilog HDL提供了一个强大的平台去描述并最终合成针对特定应用场景定制化的电路逻辑。下面给出一段简化版的一维8点DCT核Verilog代码片段作为例子说明如何用硬件描述语言表达这一数学运算过程: ```verilog module dct_1d ( input wire clk, input wire reset_n, input wire signed [7:0] data_in [0:7], output reg signed [15:0] result_out [0:7] ); always @(posedge clk or negedge reset_n) begin if (!reset_n) begin // Reset logic here... end else begin // Perform butterfly operations and other necessary computations. // This is a placeholder for actual implementation details which would involve multiple stages of computation. // Example pseudo-computation (not functional code): result_out[0] <= ((data_in[0] + data_in[7]) * 8'd29); // Repeat similar lines for all outputs based on specific algorithm chosen. end end endmodule ``` 请注意以上仅为示意性质的内容展示,并未包含完整的功能定义及错误检测机制等内容;实际项目开发过程中应当依据需求详尽考虑各方面因素后再予以完善。 ### Matlab中的DCT变换实现 如果目标是在高级脚本环境中测试DCT效果,则MATLAB提供了便捷的方法来进行此类实验。如下所示的是一个典型流程,其中涉及到了读取图片文件、划分区域并对各部分单独施加二维DCT变化等功能模块: ```matlab % Load image file into variable 'scaled_img' block_size = 8; [h, w] = size(scaled_img); dct_coefficients = zeros(h, w); for y = 1:block_size:h for x = 1:block_size:w block = scaled_img(y:min(end,y+block_size-1), x:min(end,x+block_size-1)); dct_block = dct2(double(block)); % Apply two-dimensional DCT transformation dct_coefficients(y:min(end,y+block_size-1), x:min(end,x+block_size-1)) = dct_block; end end imshow(log(abs(dct_coefficients)), []); colormap(jet(64)); colorbar; title('DCT Coefficients'); ``` 这段程序展示了怎样加载一幅灰度图象,接着按照指定大小切割画面单元格,最后逐一对它们实施DCT转换并将所得系数存回原数组位置等待后续分析处理[^2]。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值