news 2026/10/20 18:27:40

双随机相位编码结合菲涅尔变换的图像加密原理与Matlab实现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
双随机相位编码结合菲涅尔变换的图像加密原理与Matlab实现

做图像加密课题的人,十有八九会在某个晚上对着“双随机相位结构结合菲涅尔变换图像加密”这种标题发呆:到底是怎么个双随机法?菲涅尔变换明明是光学衍射里的东西,为什么会跑到加密领域来?我第一次拿到这个题目的时候也是一脸懵。后来自己动手把Matlab代码跑通,又把每个参数翻来覆去地改,才把整条链路彻底捋明白。

这篇就从原理讲到代码,再讲到我实际调试中踩过的坑。不管你是课程设计、毕业课题,还是单纯对光学信息安全感兴趣,只要想搞懂“双随机相位+菲涅尔变换”这套图像加密方案,跟着走一遍基本就够了。网上很多代码站会把这类仿真打包成“4324期”这种编号发布,打开一看其实核心就两个函数加一个主脚本,结构不复杂,但真正麻烦的是跑通之后改参数、做分析、写报告这些事。这篇权当一份参考答案。

1. 双随机相位编码的底层逻辑

1.1 一个随机相位掩模做不到的事

先说第一个随机相位掩模(RPM1)到底做了什么。假设你有一张灰度图像,数值上可以看成一个实值矩阵。给它乘上一个复相位因子 exp(i·2π·rand(M,N)),其中 rand 生成 0 到 1 之间的均匀随机数,这样每个像素的相位都被改成了随机值,但振幅完全不变。

这里有个非常容易误解的点:乘上相位掩模之后,在空域直接看强度图,图像基本还是原来的样子。因为强度等于振幅平方,相位变化不改变振幅。很多人刚接触DRPE时会困惑“这不还是能看见原图吗?哪来的加密?”答案是:加密效果不是在这一步产生的,而是在衍射传播之后才显现出来的。

相位被打乱之后,光场在自由空间中传播,不同位置的子波相互干涉,原本有序的空间结构被彻底破坏,观察面上得到的强度分布才会变成类似白噪声的图案。这就是为什么DRPE至少需要一次衍射过程,而不是简单地把图像和随机相位相乘就完事。

1.2 第二个随机相位掩模让信息彻底“溶解”

如果只有一个随机相位掩模一次衍射,严格来说系统还不够安全。原因在于:图像的空域信息和相位扰动之间存在某种可预测的关系,如果攻击者知道系统参数,单掩模方案是可以被反推的。

所以经典的双随机相位编码(DRPE)在第一次衍射之后,在变换域(菲涅尔域)再乘一个独立的随机相位掩模(RPM2),然后再做一次衍射得到密文。这相当于对信号做了两次独立的随机调制,中间还穿插了两次空间扩散。第一块掩模把图像的相位信息打乱,第二块掩模把衍射场的复振幅再次打乱。两道关卡叠加之后,输出面上的强度分布和原始图像之间几乎找不到线性痕迹。

用生活化的比喻来说:图像是一封写好的信,第一块随机相位是搅碎纸张的碎纸机,第一次衍射是把碎片撒到空中,第二块随机相位是再给每个碎片喷上乱码涂料,第二次衍射则是让这些碎片飘得到处都是。想恢复原信,必须知道碎纸机、涂料配方、飘散距离,一个都不能错。

1.3 为什么是菲涅尔变换而不是傅里叶变换

很自然会有一个疑问:教科书里的DRPE经典框架用的是4f系统,也就是两次傅里叶变换,为什么这个题目偏要用菲涅尔变换?

关键在于菲涅尔衍射多出了两个自由密钥维度:衍射距离和波长。

4f系统里,透镜的焦平面正好是输入面的频谱面,系统结构固定后,变换关系是确定的,你只能在两个随机相位掩模上做文章。菲涅尔变换不一样,它是光在自由空间中传播的自然模型,衍射距离 z 是可以任意取的,波长 λ 也是可以变的。加密方把 z1、z2、λ 作为额外密钥,解密方如果不知道这些参数,即便拿到了两个随机相位掩模,也无法正确重建图像。

另外从物理实现角度看,菲涅尔域DRPE不需要严格的透镜共轭关系,光路结构更简单,装配误差的容错性也好一些。在Matlab数值仿真中,这种差异体现在:菲涅尔变换只需要用FFT实现衍射计算,不需要构造严格满足傅里叶变换关系的共轭掩模对,随机相位掩模R1、R2可以独立生成,自由度更高。

2. 菲涅尔离散化:两种数值实现与参数选择

2.1 单次FFT和角谱法的本质区别

Matlab里模拟菲涅尔衍射,主流有两种写法:单次FFT法(S-FFT)和角谱法(也叫传递函数法TF)。

单次FFT法是从菲涅尔衍射积分的二次相位近似出发,把衍射过程化简成一次傅里叶变换。它的特点是:输出面的采样间隔会随距离变化,公式为 Δx2 = λ·z / (M·Δx1)。也就是说,传播距离越大,输出图像的物理尺寸越大,像素间隔变得越粗。

角谱法则是用频域传递函数 H(fx,fy) = exp(i·k·z) · exp(-i·π·λ·z·(fx²+fy²)) 直接作用于输入频谱,再做逆FFT得到输出。它需要两次FFT,但最大优势是输出阵列尺寸和采样间隔始终保持不变。

如果你只是模拟一次衍射,两种方法都行。但在加密解密这种往返传播场景里,使用角谱法最省心:解密时直接传入 -z,输出尺寸和加密端完全对齐,不会出现坐标错位。单次FFT由于输出尺度随距离改变,加解密之间还要做插值对齐,非常麻烦。所以我这边的经验是:DRPE仿真一律用角谱法做核心传播函数。

维度单次FFT(S-FFT)角谱法(TF)
FFT次数1次2次
输出采样间隔随z变化不变
输出面尺寸随z变化不变
加密解密往返需坐标对齐,麻烦天然对齐
适用场景单次衍射演示DRPE加解密、需要逆向传播的场景

2.2 采样条件:算不好解密必崩

角谱法听起来无脑好用,但它有一个非常关键的采样限制,很多人第一次跑出来噪声图就是栽在这里。

频域传递函数 exp(-i·π·λ·z·(fx²+fy²)) 是一个相位快速振荡的函数。离散网格能正确表示一个信号,需要满足奈奎斯特条件:相邻频域采样点之间,传递函数的相位变化不能超过 π,保守一点应该小于 π/2。

频域采样间隔是 Δf = 1/(M·Δx),频域最大频率是 f_max = 1/(2Δx)。我们可以估算最大相位变化率在频谱边缘处最激烈,具体推导不展开,最后会得到一个很实用的判据:

z ≤ M·Δx² / λ,保守取 z ≤ M·Δx² / (4λ)

打个比方。图像尺寸 M = 256,像素间隔 Δx = 15.6μm(常见空间光调制器的像素大小),波长 λ = 632.8nm。M·Δx² / λ = 256 × (15.6e-6)² / 632.8e-9 ≈ 0.096m。也就是说在保守估计下,衍射距离最好不要超过 9.6cm。如果硬要跑更大的距离,解密结果会因传递函数欠采样而严重失真。

这个公式做课程设计或实验参数预判非常有用,建议直接抄走。另一种缓解欠采样的思路是增大图像分辨率M或者增大像素尺寸Δx,但都不如直接把距离控制在安全范围内来得干脆。

2.3 两个核心Matlab函数

有了上面的理论基础,函数代码其实很短。先写角谱法主函数:

function Uout = fresnelTF(Uin, lambda, z, L) % fresnelTF - 角谱法菲涅尔衍射 % 输入: % Uin - 输入复振幅矩阵 % lambda- 波长(与L单位一致) % z - 衍射距离,可正可负,负值表示逆向传播 % L - 输入平面物理宽度 % 输出: % Uout - 衍射后的复振幅 [M, N] = size(Uin); if M ~= N error('建议使用正方形输入,方便频域网格对齐'); end dx = L / M; k = 2 * pi / lambda; fx = (-N/2 : N/2-1) / L; fy = (-M/2 : M/2-1) / L; [FX, FY] = meshgrid(fx, fy); H = exp(1i * k * z) .* exp(-1i * pi * lambda * z * (FX.^2 + FY.^2)); H = ifftshift(H); % 频域对齐,关键步骤 Uout = ifft2(fft2(Uin) .* H); end

这里ifftshift(H)是经典陷阱。fft2输出的零频分量在矩阵左上角,而我们定义的FX/FY频域网格是从负频率到正频率排列,零频在矩阵中心。如果不把传递函数H用ifftshift移到左上角,频域相乘时坐标就对不齐,解密结果会是一团乱麻。

再给一个单次FFT版本作为对比:

function [Uout, x2, y2] = fresnelSFFT(Uin, lambda, z, L1) % fresnelSFFT - 单次FFT菲涅尔衍射 % 注意:输出面采样间隔会随z变化 [M, N] = size(Uin); if M ~= N error('建议使用正方形输入'); end dx1 = L1 / M; k = 2 * pi / lambda; L2 = lambda * z * M / L1; dx2 = L2 / M; x1 = (-M/2 : M/2-1) * dx1; y1 = (-M/2 : M/2-1) * dx1; [X1, Y1] = meshgrid(x1, y1); x2 = (-M/2 : M/2-1) * dx2; y2 = (-M/2 : M/2-1) * dx2; [X2, Y2] = meshgrid(x2, y2); Uout = exp(1i * k * z) / (1i * lambda * z) ... .* exp(1i * k/(2*z) * (X2.^2 + Y2.^2)) ... .* fftshift(fft2(Uin .* exp(1i * k/(2*z) * (X1.^2 + Y1.^2)))) * dx1^2; end

注意fresnelSFFT里我用fftshift(fft2(...))来对齐输出坐标。两个函数都假设正方形输入,实际使用前把图像imresize成 M×M 即可。

3. 加密与解密全流程拆解

3.1 加密端三步走

加密流程看起来只有几个步骤,但每一步都有物理意义。完整链路如下:

第一步:输入图像 f(x,y) 与第一块随机相位掩模 R1 相乘,得到调制后的复振幅 U1 = f · R1。这一步的作用是把图像的振幅信息“托付”给一个复振幅场,相位已经被随机化。

第二步:U1 经过距离 z1 的菲涅尔衍射,得到 U2。这个衍射过程把空域信息扩散到整个观察平面,相当于第一次打散。

第三步:U2 与第二块随机相位掩模 R2 相乘,得到 U3 = U2 · R2,再经过距离 z2 的二次衍射,得到最终密文 cipher。

这里有两个细节要注意。第一,两块随机相位掩模必须是独立生成的,不能由同一个随机种子产生,否则相关性会被利用。第二,加密结果是一个复数矩阵,不是普通图像。要完整保存密文,最好直接存.mat文件,或者把强度和相位分别存成两个浮点矩阵。如果硬要转成uint8图片,相位信息就丢了,解密必然全毁。

3.2 解密端的关键细节

解密是加密的逆过程,但初学者经常在三个地方翻车:方向、共轭、取模。

方向:解密时第一次传播必须用 -z2,也就是fresnelTF(cipher, lambda, -z2, L),这是光的逆向传播。角谱法的好处在这里体现得淋漓尽致,直接传负距离即可,复用同一个函数。

共轭:第一块掩模、第二块掩模在解密时都要换成复共轭,即conj(R1)、conj(R2)。因为加密时乘的是 exp(i·φ),解密时要乘 exp(-i·φ) 才能把相位扰动抵消。

取模:原始图像是实值强度图,所以解密到最后一步用abs()取振幅即可。如果原始图像本身是复振幅,那就不能取模,需要记录完整复数场。这里以最常见的灰度图像为例,取abs()是正确操作。

3.3 完整可运行脚本

把上面所有东西串起来,一份完整的Matlab脚本长这样:

clear; clc; close all; %% 参数设置 lambda = 632.8e-9; % 波长 632.8nm L = 4e-3; % 平面宽度 4mm M = 256; % 图像像素数 z1 = 0.02; % 第一次衍射距离 2cm z2 = 0.02; % 第二次衍射距离 2cm %% 读取并预处理图像 img = imread('cameraman.tif'); img = im2double(imresize(img, [M, M])); %% 生成两个随机相位掩模 rng('shuffle'); % 每次运行都不同 R1 = exp(1i * 2 * pi * rand(M, M)); R2 = exp(1i * 2 * pi * rand(M, M)); %% 加密 U1 = img .* R1; % 第一块随机相位调制 U2 = fresnelTF(U1, lambda, z1, L); % 第一次菲涅尔衍射 U3 = U2 .* R2; % 第二块随机相位调制 cipher = fresnelTF(U3, lambda, z2, L); % 第二次衍射得到密文 %% 解密 V1 = fresnelTF(cipher, lambda, -z2, L); % 反向传播 V2 = V1 .* conj(R2); % 抵消第二块掩模 V3 = fresnelTF(V2, lambda, -z1, L); % 反向传播 decrypted = abs(V3 .* conj(R1)); % 抵消第一块掩模并取模 %% 显示 figure; subplot(1,3,1); imshow(img, []); title('原始图像'); subplot(1,3,2); imshow(abs(cipher).^2, []); title('加密结果(强度)'); subplot(1,3,3); imshow(decrypted, []); title('解密结果');

跑完之后你会看到:原始图像清晰可见,加密结果是一幅完全看不出内容的噪声图,解密结果和原图基本一致。加密结果我用的是强度图abs(cipher).^2,因为直接显示复振幅的实部或虚部意义不大,而强度是探测器实际能记录的量,但要注意:仿真中的密文是复数矩阵,含有相位信息,模拟的是“全息记录”的理想情况,不是强度探测。

4. 密钥敏感性与安全性分析

4.1 相位掩模错了会怎样

DRPE的密钥安全性主要来自两块随机相位掩模的随机性。理论上每个像素的相位有无限多种取值可能,两块掩模联合起来的密钥空间极大,暴力搜索完全不现实。

我在实验中做过最直观的测试:把解密端的R1换成一个错误掩模(重新生成一个随机矩阵),其余参数全部保持不变。解密结果立刻从清晰图像变成完全无规律的噪声。用相关系数corr2(img, decrypted)来量化,正确解密时相关系数接近1,错误掩模解密时相关系数通常不到0.05。

这就是随机相位掩模的“雪崩效应”:相位掩模哪怕是差一个像素、差一个随机种子,整个解密空间都会被彻底改变。所以实际工程中,务必把种子固定下来或者直接保存掩模矩阵,而不是每次重新生成。

4.2 距离和波长也是密钥

菲涅尔域DRPE相比传统4f系统的一个显著优势,就是距离参数参与了加密。我把 z1 从 20mm 改成 19.9mm,解密结果肉眼已经看不清内容;偏差超过 0.5mm 时,解密图基本就变成噪声了。这个灵敏度和系统参数有关,但总体趋势非常明显。

波长也是类似。把 λ 从 632.8nm 改成 633.8nm,解密效果同样大打折扣。在实际光学系统中,波长可以精确调控,距离可以精确测量,这两者作为额外密钥几乎不增加成本,却让攻击者多两个需要猜测的维度。

我个人的建议是:在做性能分析时,把“距离偏差-解密质量”曲线画出来,通常是一根急剧下降的曲线,这既是加密系统安全性的直观证据,也是答辩或报告里很加分的一张图。

4.3 这套方案的真实短板与加固思路

DRPE虽然经典,但安全性不是无懈可击的。最重要的问题是:它本质上是线性系统。整个加密过程可以表示成矩阵乘法,输入与输出之间存在线性映射关系。如果攻击者能拿到一组或多组明文-密文对,理论上可以估计出系统传输矩阵,从而在其他密文上做近似逆运算。对安全性要求极高的场景,纯DRPE是不够的。

常见的加固方向有三个:

  • 在DRPE之前先做像素置乱,比如混沌映射置乱、Arnold变换,把明文的空间相关性提前打散,破坏线性估计的前提。
  • 把明文相关的信息注入相位掩模生成过程,让掩模和具体图像绑定,避免通用密钥被一次性破解。
  • 在光学系统中放弃直接记录复振幅,改为只记录强度,解密时使用相位恢复算法。这样加密系统从线性变成非线性,抗已知明文攻击的能力显著增强,但代价是解密复杂度大幅上升。

5. 实测问题与经验速查

5.1 解密全噪声的六个检查点

我在调试过程中遇到过各种花式翻车,最常见的表现就是解密结果一片噪声。如果你也遇到这种情况,按顺序检查下面六个地方:

第一,检查解密时是否用了负距离。正向传播是加z,逆向传播一定是减z,也就是传入 -z2 和 -z1。方向错了,一切白搭。

第二,检查相位掩模是否取了共轭。解密端要用conj(R1)和conj(R2),不是原封不动再用一次R1、R2。

第三,检查密文的存储和读取方式。如果中间保存成jpg或uint8,相位信息已经永久丢失,Matlab再努力也救不回来。仿真演示请直接用.mat保存复数矩阵。

第四,检查显示前是否取了abs。解密得到的复振幅可能包含一个全局相位因子,直接imshow(real(decrypted))或imshow(decrypted, [])会看到斑驳的条纹甚至负数区域,取abs()才是正确的恢复结果。

第五,检查传递函数是否做了ifftshift。这是最隐蔽的坑,代码看着没问题,但频域坐标没对齐,解密出来一定是乱的。建议先用 z=0 自检:fresnelTF(U, lambda, 0, L)应该返回U本身。如果这一步都不成立,说明传递函数实现有bug。

第六,检查衍射距离是否超出奈奎斯特限制。前面给的公式,如果 z 太大,传递函数欠采样,加解密往返就会引入误差。这个情况的表现通常是:解密图像不是完全噪声,而是模糊、重影、边缘有异常纹路。

5.2 边缘振铃与有限孔径问题

角谱法本质上是带限运算,因为离散网格只能表示有限带宽。加上FFT隐含的周期性,如果图像内容靠近边缘,衍射计算会引入边缘振铃,解密结果边缘会出现亮的边框或条纹。

缓解办法很实用:加密前给图像做边缘padding,比如padarray(img, [M/2, M/2], 'symmetric'),让有效信息远离边界,完成整个加解密后再裁掉padding区域。我实测下来,对称填充的效果比零填充更好,因为零填充会在边界引入突变的振幅跳变,反而制造额外的高频分量。

5.3 参数调优建议

做实验时,参数不要随便拍脑袋。下面这套组合是我实测比较稳的起点:

lambda = 632.8e-9; L = 4e-3; M = 256; z1 = 0.02; z2 = 0.02;

在Matlab里,z1=z2=0.02m用上面的参数,采样条件安全,加密结果噪声特性好,解密重建质量高。如果是演示需要加密效果更明显,可以稍增大距离但注意别越过奈奎斯特边界。图像分辨率方面,M不要低于128,否则随机相位掩模的熵不够高,加密变得“不够乱”;M取512时效果更好,但内存占用和速度会差一些,主流机器跑起来没问题。

5.4 踩坑速查表

现象可能原因处理方式
解密全噪声掩模未共轭/距离方向错/频域未对齐按5.1逐项排查
解密图有清晰轮廓但偏暗显示时用了实部而不是模值改用 abs() 显示
边缘出现亮框或条纹FFT边界效应加padarray对称填充
加密结果隐约能看到原图轮廓衍射距离太短,扩散不充分增大z1、z2
解密图模糊重影距离超出采样条件减小z或增大M
彩色图加密后颜色异常没做灰度转换先rgb2gray再加密
保存再读解密失败转成了uint8或jpg改用.mat保存复数矩阵

这最后一列的解决方案,基本就是我在项目里反复用过、确认有效的办法。每一次看到解密失败,不要急着怀疑算法,先对照这张表做减法,通常能省下大量时间。


最后分享一个容易被忽略但很出效果的小技巧:展示加密结果时,直接imshow(abs(cipher).^2, [])可能因为动态范围太大而显得很暗。你可以用imshow(mat2gray(log(1 + abs(cipher).^2)))做一次对数变换,把噪声图案的细节拉出来,演示效果会好很多。另外,做密钥敏感性分析时,建议把错误距离、错误掩模、错误波长三组解密结果并排放在一张图里对比,视觉冲击力远比一长串数据强。这套方案虽然不是什么新东西,但作为光学信息安全的大门入口,依然值得亲手实现一遍——尤其当你自己把反向传播、共轭、坐标对齐这三件事全部捋顺之后,再回头看任何DRPE变体方案,都会觉得通透得多。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/20 18:27:36

SSM论坛管理系统实战:数据库设计、核心功能与部署全解析

很多人觉得SSM已经是“老古董”了,现在新项目基本都是Spring Boot Spring Cloud,谁还手动拼配置文件?但恰恰是这种“笨重”,让SSM论坛管理系统成为目前高校、培训机构和个人练手项目里出现频率最高的几个选题之一。它要求你把Spr…

作者头像 李华
网站建设 2026/10/20 18:27:10

基于YOLOv5的手势识别实战:从数据标注到实时推理

很多人做手势识别,第一反应就是手部关键点检测,把21个关键点坐标回归出来再判断手势。我之前也这么干过,绕了一圈之后反而发现,对于大多数"静态手势分类"场景,YOLOv5这种目标检测方案才是性价比最高的路线—…

作者头像 李华
网站建设 2026/10/20 18:27:09

STM32F103驱动ADS1256高精度ADC实战:寄存器级时序控制与硬件协同设计

简介:本资源是一套已验证可用的STM32F103C8T6驱动ADS1256高精度ADC的完整嵌入式开发方案,面向嵌入式初学者、电子设计爱好者及工业数据采集类项目开发者,解决24位Σ-Δ模数转换器在Cortex-M3平台上的SPI通信配置、寄存器初始化、多通道采样与…

作者头像 李华
网站建设 2026/10/20 18:26:59

基于深度学习的信道编码识别与扰码分析

做通信信号处理的人,十有八九都遇到过这种尴尬:抓回一段基带数据流,眼巴巴盯着解调之后的比特,心里只有一个问题——这到底是什么编码方式用了什么参数。再往下,如果数据里还加了扰码,那就更难受了&#xf…

作者头像 李华
网站建设 2026/10/20 18:26:56

通用智能体架构解析:从对话到执行的工程实践

1. 从"能聊天"到"能干活":通用智能体到底跨过了哪道坎大多数人第一次接触智能体这个概念,都是从对话式AI开始的。你问它一个问题,它给你一段回答,体验很流畅,但关掉窗口之后,它什么也没…

作者头像 李华
网站建设 2026/10/20 18:24:12

Agent、Workflow、Skill、MCP:四分钟搞懂AI干活的四个核心概念

1. 为什么这四个词值得你花四分钟如果你最近刷技术社区、看产品发布会,或者只是跟做开发的朋友吃饭,大概率会反复听到四个词:Agent、Workflow、Skill、MCP。它们出现的频率高到让人焦虑——好像不懂这几个词,就已经被时代甩开了。…

作者头像 李华