双随机相位编码结合菲涅尔变换的图像加密原理与Matlab实现
发布时间:2026/10/20 18:27:58 锦皓数字建站

做图像加密课题的人十有八九会在某个晚上对着“双随机相位结构结合菲涅尔变换图像加密”这种标题发呆到底是怎么个双随机法菲涅尔变换明明是光学衍射里的东西为什么会跑到加密领域来我第一次拿到这个题目的时候也是一脸懵。后来自己动手把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仿真一律用角谱法做核心传播函数。维度单次FFTS-FFT角谱法TFFFT次数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。这是最隐蔽的坑代码看着没问题但频域坐标没对齐解密出来一定是乱的。建议先用 z0 自检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里z1z20.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变体方案都会觉得通透得多。
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。