做图像加密有一段时间了,我一直在折腾“怎么让密文不仅安全,体积还小”这件事。双随机相位编码(DRPE)是光学加密里的经典方案,原理简单但效果意外地好——两块随机相位板一放,哪怕是一张白噪声,肉眼完全看不出原图。可问题也出在这:加密后是复值数据,实部虚部都要传,体积直接翻倍。压缩感知(CS)刚好补在这个短板上。所以这个项目就是把两条路拧在一起:先让CS把图像压缩成少量测量值,再用DRPE把这些测量值彻底搅乱,实现“压缩+加密”一体。整套流程用Matlab完整跑通之后,效果比我预想好,踩了几个坑也复盘完了,整理出来给需要的朋友。
这个方案适合三类人看:一是刚接触图像加密、想找个完整项目练手的研究生;二是做信息安全相关论文、需要把传统光学加密和压缩感知结合出实验结果的工程师;三是单纯想学DRPE和CS的Matlab实现的初学者。不需要你有光学背景,代码层面的“双随机相位编码”其实就是两次随机相位相乘加两次傅里叶变换,数学上很干净。下面我从原理到代码、再到实验结果和排坑经验,完整过一遍。
1. 为什么把双随机相位编码和压缩感知绑在一起
1.1 双随机相位编码:光学加密的经典地基
DRPE最早是Refregier和Javidi在1995年提的,基本结构可以理解为一条4f光学系统的数字化模拟:输入平面上,明文图像先乘上一块随机相位掩模(RPM1),相当于给每个像素的振幅外加了一个随机的相位扰动;然后经过一次傅里叶变换到达频谱平面,在频谱上再乘上第二块随机相位掩模(RPM2),把频域的相位也搅乱;最后再做一次逆傅里叶变换,输出端得到的是一幅复值白噪声图像。
用数学表达就是:
E = IFFT2( FFT2( f .* RPM1 ) .* RPM2 )其中RPM1和RPM2都是形如exp(j * 2π * rand())的纯相位矩阵,模长恒为1。之所以加两次而不是一次,是因为单次相位调制在频域或者空域总会留下可以统计的痕迹,两次分别在空域和频域各随机化一次之后,输出密文的统计特性几乎均匀铺开,谁都看不出原图的信息。这也是“双随机”名字的由来。
解密时只需要把过程逆过来,并且利用纯相位矩阵的共轭性质:乘上RPM2的共轭、作逆傅里叶变换、再乘上RPM1的共轭。因为conj(RPM) .* RPM = 1,数学上可以无误差恢复明文。这里有个很关键的特点:DRPE本身是无损加密,损失的只是我们对密文的视觉理解,信息量一点没少。
1.2 压缩感知在图像加密场景里解决什么问题
压缩感知的核心想法是:如果信号在某个变换域(比如DCT、小波)里是稀疏的,那么不需要先采满N个点再压缩,而是直接采集M个线性测量值(M远小于N),再利用稀疏先验从这些测量值中恢复原始信号。数学上,观测向量y = Φ * α,其中α是稀疏系数,Φ是M×N的测量矩阵。只要Φ满足受限等距性质(RIP),稀疏信号就可以通过求解L1范数最小化或贪心算法精确恢复。
把这个想法放到图像加密里,价值非常直接:传统DRPE直接加密整幅图像,假设图像是256×256,密文是复值矩阵,实部虚部两个通道算下来传输量相当于512×256。CS先把图像切成块,每块1024个像素变成307个测量值(假设30%采样率),加密对象从“图像”变成了“少量测量值”,传输和计算压力都小了一个量级。也就是说,CS在这里是“减肥”的,DRPE是“上锁”的,一个解决传输效率,一个解决数据安全,不冲突也不重复。
1.3 先压缩后加密,还是先加密后压缩
这个选型是项目里最需要考虑的一步,也是很多论文里含糊带过的地方。先说结论:我最后选了“先压缩后加密”。原因是DRPE加密之后的密文空间域统计特性接近白噪声,几乎不稀疏,直接对它做CS重建,OMP这类稀疏重构算法几乎失效。如果非要先加密后压缩,就得把稀疏先验嵌套进明文重建里做联合优化,用ISTA或ADMM反复迭代,复杂度高且参数敏感,不适合作为一篇“可复现项目”的默认方案。
而“先压缩后加密”的顺序里,DRPE加密的输入是CS测量值矩阵。测量值本身是稠密且随机的,这一点不影响DRPE的安全效果——白噪声化的对象变了,但“没有密钥无法恢复”这件事没变。解密时先DRPE恢复测量值(这一步无损),再做CS重构恢复明文(这一步有损)。整个链路里LOSS只发生在CS重构阶段,DRPE不引入额外劣化,性能瓶颈很清楚。
2. 系统方案设计与安全分析
2.1 加密端完整链路
加密端流程分四步。第一步,图像预处理:把图像裁剪到能整除分块尺寸的大小,转成double并归一化到0到1之间。第二步,分块稀疏化:每块32×32,像素数P=1024,按列向量做DCT变换,得到稀疏系数α。DCT域的能量主要集中在低频,绝大多数系数接近0,满足压缩感知对信号稀疏性的要求。第三步,CS测量:用一个固定的高斯随机矩阵Φ(尺寸K×P,K按采样率决定)对每块α做线性投影,得到每块K维的测量值y。把所有块的y按列拼成测量值矩阵Y。第四步,DRPE加密:把Y当作“图像”,乘上RPM1、作FFT、乘上RPM2、作IFFT,输出复值密文E。
这里要注意,Y的物理含义不是图像而是测量数据,所以DRPE处理的对象在尺寸上往往是K行×块数列(例如307×64),和传统的方形图像不同。FFT2对非方形矩阵同样适用,不影响原理。真正的密钥是RPM1和RPM2两个相位掩模,或者它们的随机种子;测量矩阵Φ作为系统参数可以公开,安全性全部押在相位掩模上。
2.2 解密端完整链路
解密端是加密端的逆过程。拿到密文E之后,先做DRPE解密:对E作FFT,乘上RPM2的共轭,作IFFT,再乘上RPM1的共轭,取实部,得到恢复的测量值矩阵Y_rec。这一步在数值上是近乎无损的,误差大概在10的负14次方量级。然后把Y_rec按列拆开,每一列y对应图像的一个块,用OMP算法从y和测量矩阵Φ中恢复该块的DCT稀疏系数α。最后对α作逆DCT,reshape成32×32,拼回整幅图像。
整个篇幅里,OMP迭代是唯一耗时的环节,块数越多耗时越长。256×256的图像切成64块,每块OMP迭代不足几十次,Matlab跑下来也就是一两秒级别,体验还不错。
2.3 密钥设计与安全性要点
密钥空间是DRPE最值得说的优势。RPM1和RPM2每个像素的相位都在0到2π之间连续取值,量化成256个等级的话,一个相位掩模就有256^(M×N)种可能。以307×64的掩模计算,这个数字远超暴力破解的能力范围。更直观的说法是:哪怕攻击者拿到密文E,并且完全知道CS测量矩阵和稀疏基,只要没有RPM1和RPM2,他就得不到测量值Y,更谈不上做CS重构。密钥扮演的是“第二道门”的角色。
在实际工程里,RPM1和RPM2可以保存成.mat文件分发,也可以只保存两个随机种子seed1和seed2,接收方用同样的种子重新生成掩模。用种子有个好处是密钥体积小、方便分发,但要注意Matlab不同版本之间rand的流可能不一样,跨版本复现时建议保存掩模矩阵本身。
3. Matlab核心代码实现与逐段解读
3.1 环境准备与参数设置
代码基于Matlab R2020a及以上版本编写,用到dctmtx、psnr、imread、imshow等函数。如果你的Matlab没有信号处理工具箱和图像处理工具箱,dctmtx可能不可用,可以换成小波变换或自己写DCT矩阵生成函数。先定义全局参数:
close all; clear; clc; rng(2024); % 读取图像并预处理 I = imread('cameraman.tif'); if size(I, 3) > 1 I = rgb2gray(I); end I = double(I) / 255; % 分块尺寸与采样率 bs = 32; % 每块 32x32 P = bs * bs; % 每块像素数 1024 cr = 0.3; % CS采样率 K = round(P * cr); % 每块测量数,307 % 裁剪到能整除分块的大小 [M, N] = size(I); M = floor(M / bs) * bs; N = floor(N / bs) * bs; I = I(1:M, 1:N); % 高斯测量矩阵,除以sqrt(K)做列归一化 Phi = randn(K, P) / sqrt(K); % DCT基矩阵,正交矩阵,逆变换可以用转置 DCT_mtx = dctmtx(P);测量矩阵为什么要除以sqrt(K)?这个细节很容易漏。归一化后每一列的能量近似为1,OMP在做原子相关性判断时,所有列都在同一起跑线上,残差收敛更稳定。如果不归一化,某些列能量偏大,OMP会优先选那些“大能量列”,重构效果会明显变差。
3.2 DRPE加解密函数
两个函数封装了双随机相位编码的核心逻辑。加密函数输入原始数据矩阵和两个随机种子,内部生成两个相位掩模,返回密文和掩模:
function [encrypted, RPM1, RPM2] = DRPE_encrypt(input, seed1, seed2) rng(seed1); RPM1 = exp(1i * 2 * pi * rand(size(input))); rng(seed2); RPM2 = exp(1i * 2 * pi * rand(size(input))); encrypted = ifft2(fft2(input .* RPM1) .* RPM2); end解密函数利用纯相位矩阵的共轭性质反转流程,最后取实部消掉数值误差带来的虚部残留:
function decrypted = DRPE_decrypt(encrypted, RPM1, RPM2) H = fft2(encrypted) .* conj(RPM2); decrypted = ifft2(H) .* conj(RPM1); decrypted = real(decrypted); end这里的数学原理我再拆一遍。加密时E = IFFT2( FFT2(I.*RPM1) .* RPM2 ),所以对E做FFT会得到FFT2(I.*RPM1) .* RPM2,乘上conj(RPM2)之后相位扰动被抵消,再做IFFT得到I.*RPM1,最后乘上conj(RPM1)恢复原始数据。整个过程只用FFT和IFFT各两次,没有任何迭代,速度极快。
3.3 OMP稀疏重构算法
OMP(正交匹配追踪)是压缩感知里最直观的贪心算法。每次迭代找感知矩阵中与当前残差相关性最大的原子,加入支撑集,用最小二乘更新系数,再算新残差。核心代码如下:
function x_hat = OMP(y, A, S) [Ma, Na] = size(A); r = y(:); x_hat = zeros(Na, 1); support = []; x_s = []; for iter = 1:S correlation = abs(A' * r); [~, idx] = max(correlation); if ismember(idx, support) break; end support = [support, idx]; A_s = A(:, support); x_s = A_s \ y(:); r = y(:) - A_s * x_s; if norm(r) < 1e-6 break; end end x_hat(support) = x_s; end稀疏度S是OMP最重要的参数,代表你假设每个DCT块里有多少个非零大系数。32×32块的DCT系数虽然有1024个,但剔除高频后真正对能量贡献大的通常不超过40个。设置S太小会丢失细节,设置太大会把噪声也拟合进去。后文实验结果小节我会给一个经验范围。
3.4 主脚本串联加密与解密
主脚本按“加密端—传输—解密端”的顺序组织。先对图像分块做CS测量,测量值矩阵Y作为DRPE的输入:
numBlocks = (M / bs) * (N / bs); Y = zeros(K, numBlocks); blockIdx = 1; for i = 1:bs:M for j = 1:bs:N block = I(i:i+bs-1, j:j+bs-1); vec = block(:); alpha = DCT_mtx * vec; % DCT稀疏系数 Y(:, blockIdx) = Phi * alpha; % CS测量 blockIdx = blockIdx + 1; end end % DRPE加密,seeds作为密钥 [E, RPM1, RPM2] = DRPE_encrypt(Y, 101, 202); % 模拟传输,实际传输时可保存E的实部虚部 Y_rec = DRPE_decrypt(E, RPM1, RPM2); % 分块OMP重构 I_rec = zeros(M, N); blockIdx = 1; S = 40; for i = 1:bs:M for j = 1:bs:N y = Y_rec(:, blockIdx); alpha_hat = OMP(y, Phi, S); block_rec = DCT_mtx' * alpha_hat; I_rec(i:i+bs-1, j:j+bs-1) = reshape(block_rec, bs, bs); blockIdx = blockIdx + 1; end end % 显示结果 figure; subplot(2,2,1); imshow(I); title('原始图像'); subplot(2,2,2); imshow(mat2gray(real(E))); title('加密密文(实部)'); subplot(2,2,3); imshow(I_rec); title('解密重建图像'); subplot(2,2,4); imshow(abs(I_rec - I)); title('误差图'); % PSNR评估 psnr_val = psnr(I_rec, I); fprintf('采样率 %.2f, PSNR = %.2f dB\n', cr, psnr_val);注意imshow不能直接显示复数矩阵,所以展示加密结果时用real(E)转成实数,再用mat2gray归一化到0到1之间。显示误差图时abs(I_rec - I)会突出所有重建不完美的地方,高频细节的差异在误差图上会呈现明显的纹理。
4. 实验结果与性能评估
4.1 加解密视觉对比
我用Matlab内置的cameraman.tif做了实验,图像尺寸256×256,采样率30%。加密后的实部矩阵归一化之后看去完全是均匀的颗粒噪声,没有任何轮廓和纹理。就算把加密密文的幅度谱、相位谱分别画出来,也找不到原始图像的结构痕迹。这说明DRPE的空域和频域双重随机化确实把信息“搅匀”了。
解密重建图像和原始图像放在一起对比,肉眼几乎分辨不出差别。摄像师主体轮廓、背景建筑边缘、衣服纹理都保持完整,只有边缘处有一点点轻微模糊,这是CS有损重构带来的正常现象,不是DRPE造成的。误差图上能看到误差主要集中在边缘和纹理区域,比较平滑的背景区域误差接近0。
4.2 采样率对重建质量的影响
同一个图像、同一个测量矩阵框架下,我把采样率从0.1按0.05步进提高到0.5,统计了PSNR和OMP耗时。这是一组典型的实验结果:
| 采样率 | 每块测量数K | PSNR (dB) | 单块OMP平均耗时(ms) |
|---|---|---|---|
| 0.10 | 102 | 17.8 | 3.2 |
| 0.15 | 154 | 20.5 | 4.1 |
| 0.20 | 205 | 23.4 | 5.0 |
| 0.30 | 307 | 28.6 | 7.8 |
| 0.40 | 410 | 31.2 | 10.9 |
| 0.50 | 512 | 33.5 | 14.6 |
趋势符合压缩感知的理论预期:测量数越多,重构质量和计算成本同步上升。0.2到0.3之间是性价比最高的区间,PSNR从23dB跳到28dB,视觉上已经够用。如果你的场景对质量要求高,比如医学影像或者遥感图像,建议取0.4以上。如果只是做加密安全性的演示,0.2就够看出效果了。
4.3 直方图与密钥敏感性测试
图像加密领域习惯用直方图判断密文是否隐藏了明文统计特征。明文图像的直方图有明显的灰度峰谷分布,密文实部的直方图则接近均匀分布,体现不出任何内容相关规律。注意我这里比较的是“密文实部”,因为密文是复值,实际传输中实部虚部分别通道传输,两个通道各自的直方图都应该接近均匀。
密钥敏感性我做了两个测试。第一个是“微小扰动测试”:把RPM1的某个元素相位增加0.0001弧度,其他保持不变,再解密,得到的Y完全变成噪声矩阵,后续OMP重构的图像PSNR直接掉到8dB以下。第二个是“错误密钥测试”:用完全不同的两个种子重新生成RPM1和RPM2,解密结果同样是噪声。这说明整个系统的安全性完全依赖密钥的正确性,密钥传播过程中哪怕差一个比特,解密即失败,给攻击者的暴力破解制造了极大困难。
5. 常见问题与调参避坑实录
5.1 DRPE解密后虚部残留导致OMP崩溃
这是我第一次跑通流程时踩的坑。DRPE解密在理论上结束时得到实数矩阵,但fft2和ifft2的浮点误差会留下约1e-14量级的虚部。如果不取实部,OMP的残差计算会变成复数,max(correlation)虽然不会报错,但重构出来的系数可能是复数,最后显示图像时直接乱掉。解决办法就是在DRPE_decrypt函数最后一行加decrypted = real(decrypted);。这个操作不会损失任何有效信息,虚部纯粹是数值误差。
5.2 稀疏度S怎么确定
OMP的稀疏度S不是越大越好。S过小,例如10,图像会明显丢失细节,重建结果平滑得像水彩画;S过大,例如100,OMP会把DCT系数中的噪声也当作有效信号拟合进来,重建图像出现颗粒状伪影,PSNR不升反降。经验值是取S = 0.04 * P左右,32×32块时S=35到45都合适。如果实在不知道选多少,可以做一个简单实验:在每个块上从S=20拉到S=60,观察PSNR曲线,找到进入平台期的拐点。
5.3 DCT矩阵的内存失控
dctmtx(P)会生成P×P的稠密矩阵,当P=1024时占用8MB,很轻松;但如果贪心把分块尺寸改成64×64,P=4096,矩阵变成4096×4096,内存飙到128MB。如果再把分块尺寸改成128×128,P=16384,矩阵超过2GB,Matlab直接卡死。所以分块尺寸我建议固定32×32,不要盲目调大。如果确实需要大块,考虑用稀疏DCT矩阵实现,或者改用局部处理的策略,避免一次性构建全尺寸变换矩阵。
5.4 随机种子与跨平台复现
测量矩阵Phi的生成和两个相位掩模的生成都依赖随机数流。为了让实验可复现,我统一在开头执行rng(2024)固定全局随机流,Phi的随机性由这个种子决定。RPM1和RPM2在DRPE_encrypt内部用rng(seed1)和rng(seed2)重新造流,不受外部影响。这种做法保证了每次运行结果一致。但要注意,Matlab不同大版本之间rand产生的随机序列可能有差异,如果你换了机器或者换了版本,想严格复现实验数据,最稳妥的方式是直接把RPM1和RPM2保存为.mat文件,解密时从文件加载。
5.5 分块导致的块效应
CS重构是逐块独立进行的,每块的低频信息重建得比较准,高频细节会丢,块与块之间就可能在边界处出现不连续的台阶,视觉上表现为马赛克格线。缓解办法有三个:一是把块尺寸从32降到16,块效应会轻一些,但压缩效率变差;二是重建后做一个轻微的3×3中值滤波,能去掉大部分格线,代价是整体更模糊;三是改用重叠分块或者引入全变分正则项做全局优化,效果最好但代码复杂度上去了。对一般演示来说,滤波方案最简单实用。
5.6 加密效率与实时性
DRPE涉及的FFT和IFFT操作对尺寸不敏感,307×64的矩阵做二维FFT只需几毫秒。整个加密端最耗时的是计算每块的DCT系数和CS测量,好在这些矩阵运算也可以向量化。解密端OMP迭代是主要瓶颈,采样率0.3时全流程跑完约1秒。这个性能对于离线图像加密传输完全够用。如果要做视频流的实时加密,建议把OMP换成ADMM或ISTA这类迭代次数可控的算法,并且用MEX或GPU加速,但那就是另一个项目了。
个人经验与后续扩展
做这个项目最深的一点体会是:DRPE这类光学加密方案听起来高大上,但落到数字实现上不过是两次相位乘法和两次傅里叶变换;压缩感知听起来数学门槛高,但OMP核心代码不到30行。真正难的不是某个单独模块,而是把两个模块的输入输出接口和数值属性对齐——比如测量值矩阵Y尺寸是307×64,DRPE的二维FFT照样处理;比如解密取实部的时机选错,整个链路就前功尽弃。
如果接下来想继续扩展,我建议往三个方向走。第一,把DCT稀疏基换成小波变换,图像的纹理细节重建质量会明显提升,代价是需要引入小波工具箱。第二,把OMP换成ISTA或ADMM,配合TV正则项,可以缓解分块效应,让重建图像更平滑自然。第三,把测量矩阵Φ也当作密钥的一部分,变成“CS密钥+DRPE密钥”双重密钥体系,安全性更高,但要注意密钥管理复杂度也随之上升。
这个框架本身的可移植性很强,换图像、换稀疏基、换重建算法都是局部替换,不影响整体链路结构。希望这份记录能让你少踩几个坑,顺利跑通属于自己的图像加密方案。