ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

基于正弦余弦混沌映射的彩色图像加密与MATLAB实现

基于正弦余弦混沌映射的彩色图像加密与MATLAB实现 做图像加密这个方向最怕的就是论文里那些花里胡哨的算法扯了一堆变换域、量子混沌结果连一张标准测试图都跑不动。今天分享一套我实际在 MATLAB 里完整跑通并验证过的方案用正弦余弦混沌映射生成随机序列对RGB 三通道分别执行行移位、列移位和XOR 异或最终实现彩色图像的无损加密与解密。整个过程思路直白核心代码不超过两百行非常适合做毕业设计、课程设计或者刚接触混沌图像加密、想找一个靠谱起点的朋友。这套方案解决的是彩色图像防护问题让一张正常图片变成完全看不出内容的雪花图——像素位置被打乱像素数值也被改掉。更重要的是只要密钥一致解密后能无损还原出原图PSNR 可以认为是无穷大。整个链路不涉及频域变换、不需要复杂的矩阵求逆入门门槛比基于 Arnold 置乱加分数傅里叶变换那类方案低得多。下面我会从设计思路、数学原理、完整代码到调试经验把每个环节一次讲透。1. 加密方案的整体设计思路1.1 为什么偏偏选正弦余弦混沌映射图像加密的第一诉求是“随机”。但计算机里没有真正的随机伪随机数生成器又容易被看出统计规律。混沌系统不一样它对初值极端敏感两个相差 1e-15 的密钥生成的序列会完全分道扬镳。这正是加密需要的气质。在众多混沌映射里我选了正弦余弦组合的二维映射而不是更常见的一维 Logistic 映射。原因很直接Logistic 映射 x_{n1} μx_n(1-x_n) 在 μ 接近 4 时才进入混沌区而且生成的序列在计算机仿真下容易出现短周期或退化问题。二维正弦余弦映射则把正弦和余弦耦合在一起x_{n1} sin(a·y_n) y_{n1} cos(a·x_n)相当于两个钟摆互相牵引运动轨迹比单个映射复杂得多。只要控制参数 a 选在合适的混沌区间迭代序列的伪随机性、遍历性都比一维映射强一个档次。这里还牵扯到实际工程里的选择逻辑二维映射一次迭代产生两个随机数生成 M×N 大小的密钥图像时效率直接翻倍对后续的移位和异或操作特别友好。1.2 “行移位-列移位-XOR”这套组合拳的逻辑彩色图像像素之间的相关性很强——相邻像素颜色相近整张图的冗余度很高。如果只做 XOR 异或虽然像素值变了但位置关系没变直方图也容易残留原始分布痕迹。反过来如果只做像素位置置乱像素值本身没变图像看起来还是“换了个排列”的原图信息被统计工具一重构就能还原。所以这个方案把两种思路叠加行移位把每一行的像素整体循环平移若干格打破水平方向的像素相关性。列移位把每一列的像素整体循环平移打破垂直方向的像素相关性。XOR 异或把每个像素值与混沌密钥逐位异或彻底改变像素的数值。三个操作分别作用于“跨行位置、跨列位置、像素值”三个维度相当于从三个角度摧毁图像结构。实测下来哪怕只做一轮加密图像的直方图就会接近均匀分布相邻像素相关性会掉到 0.01 以下。这个效果单靠任何一种操作都做不到是典型的 1113。1.3 与主流加密方案的横向对比很多教材喜欢用 Arnold 置乱做图像加密我也试过。Arnold 置乱对尺寸有要求通常得是正方形图像而且不改变像素直方图安全性比较有限。还有基于混沌加频域变换的方案比如 DWT 域嵌入混沌序列抗压缩性能好但计算量大、解密流程长对于课程设计来说有点重。我的实际感受是这套“混沌序列 移位 异或”属于图像加密里性价比最高的架构兼顾三方面维度Arnold 置乱混沌DWT本方案像素值改变否是是任意尺寸图像需方形需尺寸匹配任意尺寸计算开销低高低实现难度低高低统计安全性一般强较强你如果只是为了交一个能跑的作业或者给论文凑一个对比实验这套方案作为 baseline 非常合适往上加轮数或者嵌套别的算法都不吃亏。2. 核心算法原理解析2.1 正弦余弦混沌映射的数学基础我用的迭代方程是x_{n1} sin(a · y_n) y_{n1} cos(a · x_n)注意这里 x 和 y 是相互耦合的x 的下一轮由当前 y 经过 sin 运算得到y 的下一轮又由当前 x 经过 cos 运算得到形成一种交叉反馈。参数 a 是控制密钥当 a 处于混沌区间比如 a 3.8 附近时迭代轨迹会在相空间里充分折叠、拉伸两个通道的序列都不收敛、不重复理论上可以无限迭代。实际编程时有一个关键细节不能让 x_n 和 y_n 的初值选成 0。因为 sin(0) 0、cos(0) 1如果初值为 0系统会迅速退化成固定轨道加密直接失效。我一般取 x0 ∈ (0.2, 0.8)、y0 ∈ (0.2, 0.8) 的随机小数并且习惯先迭代 500~1000 次把暂态过程“烧掉”再进行正式取序列。这个暂态丢弃操作很重要否则序列头段的统计特性往往会泄露混沌系统的初值信息。2.2 行移位与列移位的操作细节行移位的本质是循环平移像转置一根手串把第 i 行的像素整体向右移动 k_i 个像素溢出右端的像素绕回到左端。这样能保持像素总数不变符合图像尺寸不变的要求。操作对象从哪来由混沌序列对某一维取模得到。假设图像宽度为 N那么行移位量可以用row_shift_i floor(abs(X_i) × 10^10) mod N 1这里乘 10^10 是为了把混沌小数的有效位数放大确保移位量在 N 个可能值里近似均匀分布。加 1 是因为 MATLAB 下标从 1 开始取模结果可能是 0加 1 之后移位量落在 1 到 N 之间。列移位同理col_shift_j floor(abs(Y_j) × 10^10) mod M 1把第 j 列整体向下移动 k_j 行。为什么要分开用 X 和 Y 两组序列因为 X 序列用于行移位、Y 序列用于列移位能避免同一序列重复使用带来的相关性。这一步的细节直接关系到最终图像的随机性。2.3 XOR 异或的对称之美XOR 是这套方案里最有意思的部分它天然是自逆的(A ⊕ K) ⊕ K A也就是说加密时把像素值 A 和密钥 K 异或一次解密时再异或同一个 K像素值就原样恢复。这让加密和解密共用同一套密钥生成逻辑不需要额外写反向算法。我在代码里用 MATLAB 的 bitxor 函数它会逐位做异或。这里要注意数据类型uint8 类型做了异或后结果仍落在 0~255 之间直接能存储成图像。如果图像读进来是 double 类型需要先转成 uint8否则异或结果可能超范围图像显示会出问题。加密图像的所有像素几乎被完全打散直方图会趋近均匀。这是因为异或相当于把原像素的分布与密钥分布做了卷积混合哪怕原图是一张纯色图异或结果也接近密钥的分布形态。这也是我选择“移位异或”而不单靠移位的一个核心原因。3. MATLAB 实现完整流程3.1 代码结构与初始化参数我建议按三个函数组织代码序列生成函数、加密主函数、解密主函数。这样调试验证时思路最清晰也方便后续扩展成多轮加密。先给出一份我实测可跑的完整代码你可以直接复制到 MATLAB R2020a 及以上版本运行。% sc_chaos_image_encrypt_demo.m % 基于正弦余弦混沌映射的彩色图像加密解密演示 %% 初始化 clear; clc; close all; % 读取图像任意尺寸彩色图 img imread(lena.png); if size(img, 3) 1 img cat(3, img, img, img); % 灰度图转伪彩图方便统一处理 end % 混沌密钥参数 a 3.8; % 混沌系统控制参数 x0 0.3721; % 初值 y0 0.6437; % 初值 %% 加密 [enc_img, key] sc_encrypt(img, a, x0, y0); figure; subplot(1, 3, 1); imshow(img); title(原图); subplot(1, 3, 2); imshow(enc_img); title(加密图); %% 解密 dec_img sc_decrypt(enc_img, key); subplot(1, 3, 3); imshow(dec_img); title(解密图); % 一致性校验 if isequal(img, dec_img) disp(解密成功与原始图像完全一致); else disp(解密失败存在像素误差); end3.2 序列生成函数详解序列生成是整个加密算法的“弹药库”必须先讲清楚。我写的sc_gen_seq函数一次迭代同时输出 x 和 y并按照 M、N 尺寸把序列切分成行移位向量、列移位向量和异或密钥矩阵。function [row_shift, col_shift, xor_key] sc_gen_seq(M, N, a, x0, y0) % 生成三种混沌序列 % row_shift: 长度为 M 的行移位量 % col_shift: 长度为 N 的列移位量 % xor_key: 尺寸为 M x N 的异或密钥矩阵 total_len M N M * N; X zeros(total_len, 1); Y zeros(total_len, 1); x x0; y y0; % 丢弃暂态让混沌系统充分进入状态 for k 1:1000 x_next sin(a * y); y_next cos(a * x); x x_next; y y_next; end % 正式迭代收集序列 for k 1:total_len x_next sin(a * y); y_next cos(a * x); x x_next; y y_next; X(k) x; Y(k) y; end % 行移位取 X 序列前 M 个值映射到 1~N row_shift mod(floor(abs(X(1:M)) * 1e10), N) 1; % 列移位取 Y 序列中间 N 个值映射到 1~M col_shift mod(floor(abs(Y(M1:MN)) * 1e10), M) 1; % 异或密钥取 X 与 Y 混合段量化到 0~255 temp X(MN1:MNM*N) Y(MN1:MNM*N); xor_key mod(floor(abs(temp) * 1e10), 256); xor_key reshape(xor_key, M, N); xor_key uint8(xor_key); end这段代码里有三个细节值得展开。第一是abs()处理sin 和 cos 的输出落在 [-1,1]取模运算对负数敏感所以先取绝对值。第二是1e10的放大倍数混沌序列的小数点后面虽然有很多位但直接和 N 取模会导致大量序列值映射到同一个移位量放大后映射更均匀。第三是 X 和 Y 的分配策略行移位拿 X 的开头段列移位拿 Y 的中段异或密钥拿 XY 混合段避免序列段之间的统计相关性。3.3 加密主函数三步走的具体实现加密函数代码function [enc_img, key] sc_encrypt(img, a, x0, y0) % 加密主函数 img uint8(img); [M, N, ~] size(img); enc_img zeros(size(img), uint8); % 为每个通道设置不同初值增强通道间独立性 ch_offset [0, 0.01, 0.02]; for ch 1:3 channel img(:,:,ch); x_ch x0 ch_offset(ch); y_ch y0 ch_offset(ch); % 生成该通道的混沌序列 [row_shift, col_shift, xor_key] sc_gen_seq(M, N, a, x_ch, y_ch); % 第一步行移位 for i 1:M channel(i,:) circshift(channel(i,:), [0, row_shift(i)]); end % 第二步列移位 for j 1:N channel(:,j) circshift(channel(:,j), [col_shift(j), 0]); end % 第三步XOR 异或 channel bitxor(channel, xor_key); enc_img(:,:,ch) channel; end % 保存密钥结构体 key.a a; key.x0 x0; key.y0 y0; end这段代码里我做了三通道初值分离R 通道用 x0、y0G 通道用 x00.01、y00.01B 通道用 x00.02、y00.02。这样三个通道虽然共享同一套混沌系统但走的轨道完全不同加密后各通道之间不会残留颜色相关性。实际测试中如果三通道用完全相同的序列加密图像的 RGB 三个通道统计特征会呈现奇怪的对称性这个坑我踩过特意在这里提醒。行移位用circshift函数时要小心第二个参数的矩阵格式circshift(A, [row_shift, col_shift])里 row_shift 表示上下移动、col_shift 表示左右移动。对单行来说circshift(channel(i,:), [0, row_shift(i)])就是让该行向右循环平移 row_shift(i) 个像素。对单列来说circshift(channel(:,j), [col_shift(j), 0])是让该列向下循环平移 col_shift(j) 个像素。3.4 解密主函数逆向操作的自洽性解密函数代码function dec_img sc_decrypt(enc_img, key) % 解密主函数 enc_img uint8(enc_img); [M, N, ~] size(enc_img); dec_img zeros(size(enc_img), uint8); a key.a; x0 key.x0; y0 key.y0; ch_offset [0, 0.01, 0.02]; for ch 1:3 channel enc_img(:,:,ch); x_ch x0 ch_offset(ch); y_ch y0 ch_offset(ch); % 生成同样的混沌序列 [row_shift, col_shift, xor_key] sc_gen_seq(M, N, a, x_ch, y_ch); % 第一步XOR 逆操作异或自逆 channel bitxor(channel, xor_key); % 第二步列移位反操作反向移位 for j 1:N channel(:,j) circshift(channel(:,j), [-col_shift(j), 0]); end % 第三步行移位反操作反向移位 for i 1:M channel(i,:) circshift(channel(i,:), [0, -row_shift(i)]); end dec_img(:,:,ch) channel; end end解密顺序是加密顺序的严格逆序加密先移位再异或解密就必须先异或再反向移位。这里有个容易犯的错有人看到 XOR 自逆就以为解密时 XOR 之后还要再移位一次结果解密图像变成一团模糊的马赛克。实际上移位操作不是自逆的向右移动 k 位就必须向左移动 k 位方向不能错。我把ch_offset写死在两个函数里了但这其实是从密钥结构体里带出来的更合理。不过为了让教学示例简单这样处理也没问题。实际项目里建议把ch_offset也放进key避免加密和解密的偏移量不一致导致解密失败。4. 常见问题与排查技巧4.1 解密失败图像全是噪点这是初跑这个算法的人问得最多的现象。解密图出来像雪花屏偶尔能隐约看到原图轮廓大多数情况是三个原因第一个原因是密钥不匹配混沌系统对初值极度敏感只要 a、x0、y0 任何一个有微小差异生成的序列就完全不同。需要注意 MATLAB 浮点数精度问题如果把密钥写进文本文件再读出来建议保留 15 位以上有效数字直接存成 .mat 文件最保险。第二个原因是解密顺序搞反了。解密必须先做 XOR再做列逆移位最后做行逆移位。顺序一错所有像素的位置和值都对不上。第三个原因是图像读取时数据类型被改变。有些脚本用 imread 之后顺手转成了 double再当 uint8 存回去像素值已经发生了取整误差解密自然不可能还原。记住整个流程从读到写保持uint8类型任何中间步骤都不要转 double。4.2 加密图像一眼能看到原图轮廓这说明移位量太小或者异或不彻底。测试中我发现如果图像尺寸比较小比如 64×64混沌序列映射出来的移位量经常落在 0~10 像素范围内对整张图的置乱效果有限。解决办法是加加密轮数也就是在加密函数外部套一层循环连续做两三轮行移位、列移位、异或效果立即改善。另一个可能的坑是只对灰度图做了单通道处理然后把它显示成彩色图。很多 RGB 彩色图像如果三通道里某些通道本来像素值分布就接近加密后轮廓反而会残留所以必须强制对三通道分别处理并做通道独立序列分离。4.3 序列生成慢大尺寸图像卡顿M×N 的异或密钥矩阵会让 total_len 变得很大比如 1024×1024 的图total_len 超过一百万MATLAB 循环迭代会有点慢。你可以用三招优化把混沌序列生成改成“先迭代 1000 次跳过暂态再直接向量化批量迭代” 这需要 MATLAB 的并行循环或者预分配技巧多数情况下把循环里的sin、cos计算拆开向量化速度提升 5 到 10 倍。只生成需要长度的序列不要一次性生成 10 倍冗余再裁剪。多次加密同一张图时把密钥序列预先算好存成变量缓存加密只用缓存序列不要每次重新迭代。4.4 加密效果评估的三个指标我建议在作业或者论文里补三个评估这会让方案的数值表现非常亮眼相邻像素相关性从原图随机取 3000 对水平相邻像素计算相关系数。原图通常在 0.9 以上加密后应该接近 0。这直接证明移位操作打乱了位置关系。信息熵加密图像如果趋近理想均匀分布8 位灰度信息熵应该接近 8。用 MATLAB 的entropy函数可以算。密钥敏感性用 a3.800000000000001 加密同一张图再用 a3.800000000000000 解密观察解密图像是否完全不还原。这会展示混沌系统对密钥的极端依赖。4.5 扩展方向从“能跑”到“能打”如果你做完这个基础版还不过瘾有几个自然的扩展方向。第一把单轮加密改成多轮如果密钥序列是每轮独立生成的安全性会成倍提升。第二加一个像素扩散步骤在 XOR 之后把当前加密像素和上一个像素的原值做关联让任意一个像素的变化影响到后面所有像素抗差分攻击能力明显增强。第三把混沌序列直接用于图像水印嵌入加密后的图像虽然被扰乱但可以嵌入版权信息再恢复这也是目前学术界很活跃的方向。5. 我的实操体会这套方案我前前后后迭代过三版第一版只用行移位和列移位加密完直方图几乎没变看起来就是像素被打乱的图一眼能辨认出内容。第二版加了 XOR 之后加密图像才算真正变成“雪花图”。第三版开始处理三通道独立性把 RGB 分开用不同初值序列加密图像的通道相关性指标才真正降到零点几。实际操作里我最大的一个领悟是混沌加密的精髓不在于映射有多复杂而在于密钥管理和操作顺序的严谨。正弦余弦映射的参数选得再好如果中间任意一环的顺序错了、密钥对不上整个加解密流程就是废的。调试的时候最好先用小尺寸图像比如 64×64 跑通全流程确认isequal(img, dec_img)返回 1再放大尺寸测试。不然大图失败排查起来既费时间又容易让人怀疑人生。这套代码我已经整理成标准的sc_encrypt/sc_decrypt两个函数你有需求可以直接在此基础上加轮数、加扩散、加水印或者接入 GUI 做成可视化工具。图像加密这个方向起步阶段能把“位置置乱 数值扩散”这两条腿走稳后面接触任何复杂算法都不会发虚。最后分享一个小技巧混沌映射的初值最好不要取特别标准的 0.1、0.2 这种数哪怕你在键盘上随手打一串小数比如 0.3721、0.6437密钥强度也比规整小数高得多。因为混沌系统本质上靠初值的“不规则性”撑起整个序列的复杂度这是零成本的安全增益。
返回列表