ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

小波变换图像水印的Matlab仿真:原理、QIM实现与鲁棒性分析

小波变换图像水印的Matlab仿真:原理、QIM实现与鲁棒性分析 简介面向数字水印、图像处理方向的科研人员和学生提供一套基于小波变换的水印嵌入与提取完整Matlab仿真方案。资源包含可运行源码、中文注释、参考文献以及操作步骤录屏支持Haar、Daubechies等常用小波基函数演示对载体图像进行多级小波分解后选取低频或高频系数作为嵌入区域通过阈值比较或编码方式恢复水印比特流并提供裁剪、噪声、压缩等多种攻击干扰方式便于系统评估提取效果。包内共8个文件核心为4个m函数和脚本分别承担主流程、水印嵌入、水印提取与攻击测试配合tif/jpg测试图像、mp4仿真操作视频和rar参考文献压缩包整体大小仅5.79MB。当前已有138人学习使用。借助这套资料可快速复现水印嵌入提取流程理解小波系数选择、阈值比较与比特流恢复等关键细节适合用于毕业设计或课题预研。1. 为什么图像水印都在往小波域搬在数字水印这个方向里最早被讲烂的方案是 LSB最低有效位替换——直接把水印 bit 写进载体图像的像素低位。这种方案实现简单到可以在一节课内写完但任何一次有损压缩、加噪或缩放都会把最低几位抹掉水印跟着就没了。后来大家发现把水印放到变换域里能明显提高抗攻击能力而变换域里最常用、最容易在 Matlab 里复现的就是离散小波变换DWT。和 DCT 比DWT 的优势在于它能同时拿到空间位置和频率信息低频子带集中了图像大部分能量嵌入水印后视觉损失小高频子带虽然人眼不敏感但压缩算法会先删它。所以常见做法是把水印嵌在低频系数上再用量化规则控制扰动幅度兼顾不可见性和鲁棒性。本文围绕“基于小波变换的水印嵌入和提取算法 matlab 仿真”来展开先讲清楚 DWT 系数结构和嵌入位置选择的原理再给出能直接跑的嵌入、提取函数补上 PSNR/NC 评估和 JPEG、噪声攻击的仿真代码最后聊量化步长、小波基选择这类真正影响仿真结果“看起来好不好”的参数。内容适合正在做课程设计、毕业论文的本科生也适合想把水印方案拿来做原型验证的工程师。2. 小波变换水印的核心原理与嵌入位置选择2.1 用 Matlab 获取图像的小波分解结构Matlab 里做单级 DWT 的标准函数是dwt2做多级分解用wavedec2。初学者最容易犯的错是不理解输出的排列方式[c, s] wavedec2(X, N, wname)返回的c是按近似系数加各级细节系数顺序排成的一维向量s是一个矩阵记录了每一级每个子带的尺寸必须靠索引才能把各级系数拆出来。% 读取灰度载体图像并转为 double 类型 X imread(lena.png); if size(X, 3) 3 X rgb2gray(X); end X im2double(X); % 3 级小波分解 [c, s] wavedec2(X, 3, db4); % 提取第 1 级到第 3 级的近似系数低频 LL A1 appcoef2(c, s, db4, 1); A2 appcoef2(c, s, db4, 2); A3 appcoef2(c, s, db4, 3); % 提取各级水平、垂直、对角细节系数 [H1, V1, D1] detcoef2(all, c, s, 1); [H2, V2, D2] detcoef2(all, c, s, 2); [H3, V3, D3] detcoef2(all, c, s, 3);appcoef2和detcoef2是按名称提取子带的安全方式比自己手工算s矩阵的偏移量可靠。wname选db4是 Daubechies 4 阶小波它在平滑性和紧凑支撑之间比较均衡图像水印仿真里最常见的默认选择就是db4或haar前者效果好后者实现最简。im2double把像素归一化到 0~1 区间后面的量化嵌入都建立在这个尺度上。2.2 为什么低频 LL 子带是水印嵌入的主战场小波分解后LL 子带是原图的低分辨率近似视觉内容的主要能量都集中在这里。把水印嵌进 LL 系数相当于在“图像的主干”上做微调只要幅度控制得当人眼几乎察觉不到变化而 JPEG、低通滤波这类攻击恰恰也会保留低频成分所以水印不容易被滤掉。高频细节子带虽然嵌入后不可见性更好但一旦经过有损压缩或缩放高频系数往往被削到接近零水印也随之丢失。为了把不可见性和鲁棒性的权衡讲得更具体可以用下面的表格来对比三个方向子带的特性子带能量占比人眼敏感度压缩后保留程度适合嵌入强度LL低频近似最高较敏感很强中低LH/HL中频细节中等中等中等中等HH对角高频最低最不敏感差不建议单独使用如果只做一层分解LL 子带尺寸是原图的四分之一一个 512×512 的图像LL 就是 256×256足以放下一张 64×64 的二值水印。需要更大的水印容量时就做三级分解把水印嵌到A3上A3 尺寸只有原图的 1/64能容纳的水印比一层分解小得多。所以层级选择本质上是水印容量和鲁棒性之间的取舍层级越深鲁棒性越好但容量越小。2.3 三类常见嵌入规则加法、乘法与量化索引调制确定嵌入位置后要解决的核心问题是“怎么把水印 bit 写进系数里”。三种主流规则的写法差异很大直接决定提取时需不需要原始载体图像。加法嵌入的公式是C C alpha * w其中C是原系数w是水印值通常映射为 1 和 -1alpha是嵌入强度。提取时需要原始系数C属于非盲水印好处是实现简单坏处是应用场景受限。乘法嵌入的公式是C C * (1 alpha * w)本质上是对加法规则做了一次幅度自适应系数大的地方改动量大系数小的地方改动量小对缩放攻击的抵抗稍好但仍然是非盲的。量化索引调制QIM的做法是把系数落到预设的量化网格上用网格的奇偶性表示水印 0 或 1。嵌入公式为C round((C delta/2) / delta) * delta嵌入 1或C round(C / delta) * delta嵌入 0其中delta是量化步长。提取时只判断mod(round(C/delta), 2)就能得到水印 bit根本不需要原始图像属于盲提取。代价是嵌入失真比加法规则略大且delta的取值直接决定鲁棒性和不可见性的平衡点。从实际做仿真的角度出发推荐优先实现 QIM因为盲提取比非盲提取更有说服力展示效果也更好讨论。后面的完整代码就是以 QIM 为主线来写的。3. 用 Matlab 跑通水印嵌入与提取全流程3.1 水印预处理二值化、缩放与补零水印在读入后必须处理成和 LL 子带尺寸兼容的二维二值矩阵。常见做法是把任意尺寸的水印图缩放到目标尺寸再做二值化。如果水印原图带有灰度或彩色内容二值化这一步还会丢信息所以仿真里最常用的水印素材是二值 logo 或随机 0/1 矩阵。function wm preprocess_watermark(wm_orig, target_rows, target_cols) % 缩放并二值化水印图像 % 输入 wm_orig 可以是图像路径或路径字符串 if ischar(wm_orig) || isstring(wm_orig) wm_orig imread(wm_orig); end if size(wm_orig, 3) 3 wm_orig rgb2gray(wm_orig); end wm imresize(wm_orig, [target_rows, target_cols]); wm imbinarize(wm); % 自动阈值二值化输出 logical 矩阵 wm double(wm); % 转成 double 方便数值运算 wm(wm 0) -1; % 将 0 映射为 -11 保持 1 end这里把水印映射成 -1 和 1 的取值而不是 0 和 1原因是 QIM 的嵌入判断里用“奇数网格 1偶数网格 0”只有二值逻辑不涉及正负号但在加法嵌入里正负号的区分会更方便。整套预处理逻辑的核心是保证水印矩阵能和嵌入子带逐元素操作维度不一致是新手最常见的报错原因。如果imresize之后尺寸仍差一个像素用wm wm(1:target_rows, 1:target_cols)强制裁剪即可。3.2 嵌入端实现QIM 量化 LL 系数嵌入函数的输入有三个载体图像X、二值水印wm、量化步长delta。输出是含水印图像X_w和嵌入过程中用到的子带信息。为了后续提取方便这里选择把水印放在三级分解后的A3子带上。function [X_w, A3_mod] embed_watermark_qim(X, wm, delta) % 三级 DWT 分解 [c, s] wavedec2(X, 3, db4); A3 appcoef2(c, s, db4, 3); [rows, cols] size(A3); % 调整水印尺寸与 A3 一致wm 的元素一定要在 1 和 -1 之间 wm_adj reshape(wm(1:rows*cols), rows, cols); % QIM 嵌入核心 A3_mod A3; for i 1:rows for j 1:cols q round(A3(i, j) / delta); if wm_adj(i, j) 1 % 水印 bit 为 1量化到奇数网格 if mod(q, 2) 0 q q 1; end else % 水印 bit 为 -1即 0量化到偶数网格 if mod(q, 2) 1 q q 1; end end A3_mod(i, j) q * delta; end end % 把修改后的 A3 放回原分解向量重建图像 c(1:rows*cols) A3_mod(:); X_w waverec2(c, s, db4); end这段代码最需要解释的部分是c(1:rows*cols) A3_mod(:)。wavedec2返回的c向量最前面正好是最深层近似系数的按列展开直接替换这一段不会影响细节系数。mod(q, 2)判断奇偶时如果q是偶数且水印是 1就加 1 跳到奇数网格如果q已经是奇数且水印是 1就保持不动。这里的delta是整个仿真最重要的参数它控制量化网格的间距delta越大抗攻击能力越强但载体质量下降越明显。一般的经验范围是 0.02~0.1代码里后续会用扫描法帮你选出合适值。3.3 提取端实现盲提取与几种常见的不匹配问题盲提取的流程是对含水印图像做同样的三级 DWT取 A3 系数对每个系数做round(A3_w / delta)再判断奇偶。奇数对应水印 1偶数对应水印 -1即原 0 值。function wm_ext extract_watermark_qim(X_w, delta, target_rows, target_cols) % 与嵌入端保持完全一致的分解设置 [c_w, s_w] wavedec2(X_w, 3, db4); A3_w appcoef2(c_w, s_w, db4, 3); [rows, cols] size(A3_w); wm_ext zeros(rows, cols); for i 1:rows for j 1:cols q round(A3_w(i, j) / delta); if mod(q, 2) 1 wm_ext(i, j) 1; else wm_ext(i, j) -1; end end end wm_ext wm_ext(1:target_rows, 1:target_cols); end如果提取出来的水印全是噪声或完全反色优先检查三处。第一分解层数和嵌入端是不是一致一个用了三层另一个用两层提取必然失败。第二提取端的delta必须和嵌入端完全一致delta差 0.005 都可能导致大量误判。第三水印映射关系是否一致——嵌入时把 0 映射成 -1提取时也必须把偶数判成 -1否则算 NC 时会得到完全错误的结果。这三点是 QIM 水印仿真里最经典的“代码看起来对但结果不对”的根源。4. 指标评估与攻击鲁棒性仿真4.1 用 PSNR 和 NC 量化不可见性与提取保真度嵌入后的水印图像质量用 PSNR峰值信噪比衡量提取水印和原始水印的相似度用 NC归一化相关系数衡量。Matlab 的psnr函数位于图像处理工具箱NC 需要自己写因为内置函数没有直接对应项。function [psnr_val, nc_val] evaluate_watermark(X, X_w, wm_orig, wm_ext) % 计算 PSNR输入必须是 0~1 范围内的 double 图像 psnr_val psnr(X_w, X); % 计算 NC先把 -1 映射回 0 wm1 double(wm_orig(:)); wm2 double(wm_ext(:)); wm1(wm1 -1) 0; wm2(wm2 -1) 0; nc_val sum(wm1 .* wm2) / sqrt(sum(wm1.^2) * sum(wm2.^2)); endPSNR 的合理底线一般是 30 dB低于这个值肉眼通常能看出嵌入了水印。NC 的范围是 0~1大于 0.75 时提取的水印已经能辨认大于 0.9 视为提取成功。注意nc_val的分母用了几何平均好处是当两个矩阵任一全为零时结果自然为 0不会出现除零错误。这里要考虑边界情况如果wm_orig和wm_ext完全相等NC 应该等于 1如果完全不相关NC 接近 0。4.2 JPEG 压缩攻击与高斯噪声攻击仿真鲁棒性仿真就是在嵌入完成后先做一个攻击操作再对攻击后的图像做提取。JPEG 压缩是最重要的测试项因为它在实际场景中最常发生。Matlab 直接imwrite成 jpg 再读回即可注意质量参数quality越低攻击越强。% 嵌入测试 X im2double(imread(lena.png)); wm_orig preprocess_watermark(logo.png, size(A3, 1), size(A3, 2)); [X_w, ~] embed_watermark_qim(X, wm_orig, 0.05); % JPEG 压缩攻击质量设为 50 imwrite(X_w, watermarked.jpg, Quality, 50); X_att im2double(imread(watermarked.jpg)); % 高斯噪声攻击 X_att2 imnoise(X_w, gaussian, 0, 0.001); % 分别提取并评估 wm_ext extract_watermark_qim(X_att, 0.05, size(wm_orig, 1), size(wm_orig, 2)); wm_ext2 extract_watermark_qim(X_att2, 0.05, size(wm_orig, 1), size(wm_orig, 2)); [~, nc_jpeg] evaluate_watermark(X, X_att, wm_orig, wm_ext); [~, nc_noise] evaluate_watermark(X, X_att2, wm_orig, wm_ext2);一组典型的仿真结果是delta 0.05时 PSNR 在 34~38 dB 之间JPEG 质量 50 压缩后 NC 保持在 0.9 左右高斯噪声方差 0.001 时 NC 在 0.8 以上。如果 NC 掉到 0.6 以下优先增大delta但 PSNR 会同步下降。这段代码把压缩和噪声两个攻击放在一起跑能直观看到同一组参数在不同攻击下的表现差异。4.3 常见失败模式为什么 NC 一直是 0 或 NaN排错要有顺序。最先检查wm_ext是不是全 1 或全 -1如果是问题大概率出在提取端拿到了错误的子带位置检查appcoef2的分解层数其次检查wm_orig里有没有 NaNimresize和imbinarize一般不会产生 NaN但imread读到损坏文件时会出现再次检查 NC 计算是否出现除零分母中任一矩阵全 0 就会 NaN用上面的几何平均写法可以规避。另一个隐蔽问题是量化步长过小导致误判。delta取 0.005 时量化网格极密嵌入端修改系数的幅度很小攻击后系数稍微偏移就会越过网格边界提取时判到错误的奇偶性上。这类问题在代码层面没有任何 bug纯粹是参数的问题需要靠下一章的扫描方法定位。5. 决定水印成败的量化步长与小波基选择技巧5.1 用扫描法确定量化步长的甜点区间把delta从一个很小的值逐步增大记录每个取值下的 PSNR 和 NC是最直观的参数选择手段。把攻击条件固定成 JPEG 质量 50delta从 0.01 扫到 0.15每隔 0.01 跑一次嵌入、攻击、提取最后画两条曲线——PSNR 随delta递减NC 随delta递增交点附近就是“甜点”。deltas 0.01:0.01:0.15; psnr_list zeros(size(deltas)); nc_list zeros(size(deltas)); for k 1:length(deltas) [X_w, ~] embed_watermark_qim(X, wm_orig, deltas(k)); imwrite(X_w, tmp_attack.jpg, Quality, 50); X_att im2double(imread(tmp_attack.jpg)); wm_ext extract_watermark_qim(X_att, deltas(k), size(wm_orig, 1), size(wm_orig, 2)); [psnr_list(k), nc_list(k)] evaluate_watermark(X, X_w, wm_orig, wm_ext); end % 输出结果表格 T table(deltas, psnr_list, nc_list, VariableNames, {delta, PSNR, NC}); disp(T);扫描结果通常呈现这样的规律delta0.01时 PSNR 接近 45 dB但 NC 只有 0.5delta0.08时 NC 超过 0.95PSNR 掉到 30 dB 附近。你可以按自己的需求挑选折中位置——更看重不可见性就取小步长更看重鲁棒性就取大步长。这个扫描方法也适合推广到其他参数比如换成不同小波基重新跑一遍。5.2 小波基类型与分解层数怎么配合wavedec2支持的小波基种类很多但图像水印仿真里真正常用的只有haar、db2、db4和sym4。Haar 小波的优点是实现最简单、计算最快适合快速验证算法流程缺点是它的小波函数不连续嵌入后图像容易产生块状痕迹。Db4 在平滑性和计算量之间最平衡是论文里出现频率最高的选择。sym4比db4的对称性更好重建图像的边缘失真更小但代码跑起来稍慢。分解层数方面一层分解适合水印较大的场景比如 128×128 的水印两层分解适合 64×64三层分解适合 32×32 或更小。层数再深虽然鲁棒性更强但 A4 子带只有原图的 1/256512×512 的图像只能嵌 16×16 的水印视觉上没有实用意义。建议先用三层分解跑通流程再降到两层对比效果不要直接上四层。5.3 嵌入强度自适应把水印写进人眼不敏感的区域固定delta的 QIM 属于均匀量化对图像平坦区域和纹理区域用同样的改动幅度。平坦区域的微小量化误差容易被感知而纹理区域则不明显。改进思路是把delta按区域特性做自适应计算 LL 子带每个局部块的方差方差大的地方用大步长方差小的地方用小步长。提取端需要保存步长矩阵作为密钥这会让提取从“盲”退化为“带密钥”但换来了更好的不可见性和鲁棒性平衡。另一种更实际的操作是先用edge函数检测载体图像的边缘只在边缘区域嵌入水印。边缘是人眼不敏感的区域同时边缘在 JPEG 压缩中保留程度较好。这样做的副作用是水印容量下降需要把水印信息通过纠错编码或重复嵌入来弥补。对于课设和论文建议先跑通固定delta方案再在最后一章或未来工作里指出自适应方案的改进空间。验证方式也值得提一句对提取出的水印做二值化显示用imshow直接观察结合 NC 数值判断是否肉眼可辨。最终交付的仿真成果里代码注释和参考文献要和小波基选择、攻击方式一一对应不要笼统写“参考文献”四个字而是写清楚哪个公式或哪个参数设定来自哪一篇文献这会让整个仿真工作的可信度明显提升。本文还有配套的精品资源点击获取
返回列表