
简介基于Matlab的DWT算法彩色图像二值水印嵌入与提取仿真包围绕彩色图像在离散小波变换域中的水印嵌入、提取及常见攻击测试展开适合计算机、电子信息工程、数学等专业的学生作为课程设计、期末大作业或毕业设计的参考资料。压缩包共38个文件内含20个m脚本与函数、16个bmp载体及水印效果图、1个jpeg压缩样例和1份doc课题报告整体仅1015KB轻量便携便于快速部署调试。m脚本覆盖水印嵌入、提取以及旋转、裁剪、噪声、滤波、JPEG压缩等多类攻击模拟并附带PSNR、NC等评价指标计算代码可直观评估攻击前后的水印差异。bmp图像包含原始载体、二值水印、嵌入后图像及各攻击后的输出结果适合逐模块对照学习课题报告则系统梳理DWT算法原理、实验步骤与结论脚本注释清晰具备一定Matlab基础的学习者可自行修改参数、扩展攻击类型深入理解水印系统的鲁棒性。已有236人学习下载适合作为数字水印方向的入门实操与课程设计参考。1. 为什么用 DWT 在彩色图像里藏二值水印说个反直觉的事实直接在空间域往像素值上加一个常数做水印嵌入速度最快、肉眼几乎看不出来但随便一次 JPEG 重压缩就能把水印抹掉大半。这正是 DWT离散小波变换方案存在的意义——它把图像从“每个像素的亮度”翻成“不同频率的分量”人眼敏感的粗糙结构集中在低频细节纹理散落在高频水印可以嵌在那些被压缩算法删得最晚的成分上。彩色图像相比灰度图多出两个通道嵌入位置更多视觉隐蔽性反而更好。这篇文章的目标读者不是做图像理论研究的而是要把算法跑通、把数据写进课题报告的人。也就是说读完你要能自己在 Matlab 中完成“彩色图选通道 → DWT 分解 → 嵌入二值水印 → 逆变换重构 → 提取 → 加攻击测试”的完整链路并拿到一组能写进文档的评估指标。这套流程在 Matlab 图像处理大作业、毕业设计和数字水印课程项目中都是最常被翻牌子的骨架下面所有代码按 Matlab 2020b 及以后版本的函数签名来写更早版本把个别参数名调整一下也能用。2. DWT 原理与彩色图像子带选择做对这一步后面省一半事2.1 DWT 一次变换后图像发生了什么离散小波变换处理图像时本质上是沿着行和列各做一次高低通滤波再两两组合抽样最终把原图分解成四个尺度减半的子带。Matlab 里dwt2一次调用就把这件事封装好了I imread(lena.png); if size(I, 3) 3 I rgb2gray(I); % 先转灰度看清楚分解结构 end I im2double(I); % 统一到 double避免 uint8 截断 [cA, cH, cV, cD] dwt2(I, haar);这四行做的事是读入图像若为彩色则先转灰度再转成 double 精度最后用 Haar 小波做一层分解。四个输出变量的含义需要记牢cA 是低频近似原图能量基本都在这里尺寸是原图的二分之一cH 是水平高频保留的是列方向上的变化对应垂直边缘cV 是垂直高频对应水平边缘cD 是对角高频两个方向的突变都包含能量最小。人眼对 cA 的改动相当敏感因为里面的平均亮度信息直接决定画面的明暗基调对 cD 的改动几乎注意不到但 JPEG 压缩恰恰会优先丢弃这份高频数据。于是方案选择就变成嵌在 cA 是为了扛压缩代价是可见性风险嵌在 cD 是为了隐蔽性代价是压缩后提取不出来。多数课程设计最后会折中到 cA因为对二值水印来说低频部分受攻击影响最小提取正确率最稳。为避免“看不见但丢了”两头不讨好下面统一采用低频嵌入后续实验也更容易解释。2.2 彩色图像水印用哪个通道、嵌哪个子带彩色图有三个通道不建议三个通道全嵌同一份水印否则嵌入量翻三倍、可见性明显变差。常见做法是只挑一个通道嵌入其余两个通道原样保留。选通道时有三条路可走直接选 G 通道。人眼对绿色最不敏感RGB 模型下可以在 G 上给稍大一点的强度转 YCbCr选 Y亮度通道。亮度通道和灰度图最接近算法从灰度迁移过来不用大改转 HSV 或 Lab选明度通道本质上和第二条理由相同。下表按“代码工作量 vs 视觉效果”排了个对比实际选型时可参考嵌入通道代码成本不可见性适合场景RGB 的 G 通道极低较好课程设计、快速出结果YCbCr 的 Y 通道低好想体现彩色处理流程完整性HSV 的 V 通道中好但 H/S 通道有色偏风险需要额外分析色度分量考虑到标题就是“彩色图像”建议走 YCbCr 或 G 通道理由很实际省去灰度转换后提取端还要把 RGB 重建回去通道单一的处理逻辑最简单。如果课题报告想强调“利用彩色特性提高嵌入容量”可以在后续做多通道冗余嵌入但第一版先把单通道链路彻底跑通。2.3 在 Matlab 里把图像和 DWT 结构对齐初学者最容易在这块翻车dwt2之后 cA 的尺寸是原图行列各除 2而水印图像尺寸往往是 64×64 或 128×128不处理就报维度不匹配。所以嵌入前必须做两件事把原图长宽调整成偶数因为 Haar 变换要求分层时尺寸能被 2 整除再把水印缩放或裁剪成与 cA 一致。另一个隐藏问题是dwt2默认使用周期延拓这对水印嵌入影响不大但做多层分解时延拓方式要统一否则重构后边界会出现误差。以下代码先把“读图、选通道、取子带”的骨架搭好% 读入彩色宿主图并统一尺寸 host imread(host.bmp); host imresize(host, [512 512]); % 分离 RGB 通道只嵌入 G 通道 R host(:, :, 1); G host(:, :, 2); B host(:, :, 3); % 对 G 通道做单层 Haar DWT Gd im2double(G); [cA, cH, cV, cD] dwt2(Gd, haar); % 查看子带尺寸以适配水印 fprintf(cA 尺寸: %d x %d\n, size(cA, 1), size(cA, 2));到这里嵌入所需的全部原材料已经摆好了三个原通道、一个拆好的低频子带、三个高频子带。imresize把宿主图强制改成 512×512 是一种省事做法真实场景更推荐记录原始尺寸、嵌入时只做偶数修正这样报告里能留着“图像尺寸自适应”的细节可写。3. 用 Matlab 实现 DWT 二值水印嵌入从函数调用到参数设置3.1 嵌入流程的 5 个步骤从上一章的数据结构出发嵌入流程可以压缩成五步顺序不能乱因为水印恢复时所有步骤都是逆着来的读取彩色宿主图和水印二值图把宿主图修正为偶数尺寸分离通道选定嵌入通道并转换为 double对该通道执行一层dwt2拿到 cA/cH/cV/cD将水印二值矩阵映射成 1/−1 后按嵌入规则叠加到 cA用idwt2重构该通道合并回 RGB另存为待检测图像。第 4 步是整个算法的核心。二值水印的像素取值是 0 和 255直接把它当成数值加到 cA 上会破坏小波系数的分布提取也会很别扭。常规做法是先把二值图归一化水印位置上的 255 映射为强度因子 α0 映射为 −α。这样嵌入操作变成向 cA 上叠加一个取值 ±α 的矩阵视觉变化与 α 正相关提取时靠符号判断原水印是黑还是白。3.2 嵌入规则与强度因子怎么定嵌入规则分加性和乘性两种。加性写法最简单cA cA α·W其中 W 是取值为 ±1 的水印矩阵α 通常设在 0.01 到 0.05。这个量级下cA 的系数变化最多 5%肉眼基本看不出细节损失。乘性写法则更贴合人眼对亮度比例变化敏感的机制cA cA·(1 α·W)两种形式实现上都可行乘性方式在逆小波重构后暗区的绝对误差更小对夜景或暗部图像更友好。课题报告里写“采用乘性嵌入以利用视觉掩蔽特性”会更耐看但代码上只是参数一个乘法的问题。下表是常见强度配置的直观对照alpha 取值不可见性PSNR 参考抗 JPEG 压缩能力建议场景0.0145 dB 以上几乎无感一般追求隐蔽性的演示0.0340 dB 左右较好默认推荐0.0538 dB 上下更强报告强调鲁棒性时使用0.08 以上逼近 35 dB可见纹理强但图像劣化明显不建议用在人脸等平滑区域还要注意嵌入深度这个概念α 在嵌入和提取时是否要严格一致。盲提取场景下检测方不知道 α只靠符号判断所以 α 只影响嵌入端若做成非盲提取检测端要把原始载体也输入进来此时 α 才需要两边对齐。不要为了追求提取率无限调大 αα 超过 0.08 后平滑区域会出现可见块状纹理这在课题报告里是个明显失分点。3.3 完整的嵌入代码与逐行说明下面这段代码是可以直接放进脚本跑通的完整嵌入实现宿主图用host.bmp水印用watermark.bmp输出为watermarked.bmpfunction watermarked dwt_embed(host_path, wm_path, alpha, ch) % DWT 二值水印嵌入 % 参数: % host_path: 彩色宿主图像路径 % wm_path : 二值水印图像路径 (0/255) % alpha : 嵌入强度, 建议 0.01~0.05 % ch : 嵌入通道, G 或 Y host imread(host_path); host imresize(host, [512 512]); % 读水印并转成二值逻辑型 wm imread(wm_path); if size(wm, 3) 3 wm rgb2gray(wm); end wm imbinarize(wm); if strcmpi(ch, G) R host(:, :, 1); G host(:, :, 2); B host(:, :, 3); target im2double(G); [cA, cH, cV, cD] dwt2(target, haar); % 水印缩放到低频子带尺寸 wm_resized imresize(double(wm), size(cA), nearest); wm_map (wm_resized - 0.5) * 2; % 0 - -1, 1 - 1 cA_wm cA alpha * wm_map; G_wm idwt2(cA_wm, cH, cV, cD, haar); G_wm im2uint8(G_wm); watermarked cat(3, R, G_wm, B); else % YCbCr 模式: 对 Y 通道做同样替换 YCbCr rgb2ycbcr(host); Y im2double(YCbCr(:, :, 1)); [cA, cH, cV, cD] dwt2(Y, haar); wm_resized imresize(double(wm), size(cA), nearest); wm_map (wm_resized - 0.5) * 2; cA_wm cA alpha * wm_map; Y_wm im2uint8(idwt2(cA_wm, cH, cV, cD, haar)); YCbCr(:, :, 1) Y_wm; watermarked ycbcr2rgb(YCbCr); end imwrite(watermarked, watermarked.bmp); fprintf(嵌入完成输出 watermarked.bmp\n); end这段函数最值得解释的是wm_map (wm_resized - 0.5) * 2这一行。二值逻辑值经过double之后是 0 和 1减 0.5 后变成 −0.5 和 0.5再乘 2 就得到 −1 和 1。这样 cA 上加 alpha 或减 alpha 的幅度被量化到同一个绝对值提取时只看系数偏移方向就能判位。通道合并部分有两个边界情况要处理。一是idwt2重构出的矩阵可能包含略小于 0 或大于 1 的值im2uint8会自动截断二是相邻两次dwt2和idwt2如果延拓方式不一致重构边界会出现细微亮线所以一个项目里固定用默认延拓不混用不同模式。函数已把宿主图强制改为 512×512若原图本身就是这个尺寸imresize不会改动像素值不必担心精度损耗。4. 提取算法与攻击测试用代码验证水印会不会被冲掉4.1 盲提取与非盲提取的选择提取端有两条路线。非盲提取要求检测时手里有原始宿主图和原始水印做法是“原始 cA − 待检 cA”得到差值矩阵再按符号还原水印鲁棒性最好但应用受限。盲提取不依赖原始图像检测时直接对待检图做 DWT将 cA 与基于统计的阈值比较。课程项目里通常要求做盲提取这才符合数字水印“检测时不占用原图”的实际意义。盲提取的难点在于JPEG 压缩、加噪之后 cA 的系数已经偏移不能再用 0 做固定阈值而是要对每个位置的邻域计算局部参考。一种实用做法是对待检 cA 做中值滤波用滤波结果近似“未嵌入水印的 cA”。原理是中值滤波在平坦区域保留原值在突变边缘取中间值而水印嵌入造成的 ±α 扰动尺度远小于图像自身的结构变化所以滤波结果能近似无扰动的低频分量。提取式可写成w_extracted(i,j) sign(cA_detected(i,j) - medfilt2(cA_detected(i,j), [3 3]))两个值的正负号对应嵌入时的 1 或 −1。这样做的优势是即使 JPEG 压缩把高频子带全清掉低频 cA 的均值结构还在符号关系大部分能保留。4.2 在 Matlab 里模拟 JPEG 压缩、噪声与裁剪攻击攻击测试是水印课题报告里最有说服力的环节。攻击不是把图搞花就完事要为每种攻击定义“剂量”否则很难横向对比。常见攻击清单如下攻击类型模拟方法典型剂量JPEG 压缩imwrite 中设 Quality50, 70, 90高斯噪声imnoise(gaussian, 0, var)var0.001 起中值滤波medfilt23×3, 5×5裁剪中心或角落置黑面积 25%, 50%缩放攻击imresize 放大再缩小512→256→512模拟代码把攻击封装成独立函数方便套循环批量跑function attacked apply_attack(img, type, params) switch type case jpeg imwrite(img, tmp_q.jpg, Quality, params.q); attacked imread(tmp_q.jpg); case gaussian attacked imnoise(img, gaussian, 0, params.var); case median attacked medfilt2(img, [params.size params.size]); case crop attacked img; sz size(attacked); r round(sz(1) * params.ratio); attacked(1:r, 1:r, :) 0; case resize attacked imresize(imresize(img, 1/params.factor), ... [size(img,1) size(img,2)]); end end“剂量”参数放在 struct 里而不是写成一大串函数入参好处是批量测试时可以生成参数数组然后直接循环调用。比如 JPEG 质量 50/70/90 三档每档都调一次apply_attack结果统一记录到表格里报告里就可以画出一条“提取率 vs 压缩质量”的曲线。需要注意 JPEG 攻击必须用imwrite把图落盘再读回直接对内存数组做 JPEG 模拟的函数在较新版本中已经移除不要依赖旧工具箱接口。另外imnoise的高斯噪声方差是相对 0 到 1 归一化范围定义的传参数时不需要手动转换数据类型。4.3 提取代码与相关系数评估提取水印后光靠肉眼很难判断还原质量需要两个量化指标误码率BER和归一化相关系数NC。BER 统计错位像素占比NC 衡量提取水印与原始水印的结构相似度NC 越接近 1 表示恢复越完整。Matlab 里直接用corr2就能算 NC不用手写协方差公式。function [wm_extracted, nc] dwt_extract(attacked_path, wm_original, alpha) % 盲提取: 利用中值滤波构造参考 cA att imread(attacked_path); att imresize(att, [512 512]); G im2double(att(:, :, 2)); [cA, cH, cV, cD] dwt2(G, haar); cA_ref medfilt2(cA, [3 3]); diff cA - cA_ref; % 按正负还原二值水印 wm_bin double(diff 0); wm_original_rs imresize(double(wm_original), size(wm_bin), nearest); wm_extracted imresize(wm_bin, size(wm_original), nearest); % 二值化后计算误码率与相关系数 ber sum(wm_extracted(:) ~ wm_original_rs(:)) / numel(wm_original_rs); nc corr2(double(wm_extracted), double(wm_original_rs)); fprintf(BER%.4f, NC%.4f\n, ber, nc); end注意diff 0得到的是逻辑型和 uint8 水印直接求 BER 会类型不匹配所以评估前把两者统一到 double。更稳的做法是嵌入式也返回wm_map这样提取端直接用符号比较函数避开阈值偏移问题。这两种方案在报告中分别对应“阈值零检测”和“局部参考检测”后者更适合应对噪声和压缩攻击。练习建议先固定 alpha0.03跑 JPEG Quality50/70/90 三档把 BER 画成折线。正常结果应该是 Quality90 时 BER 小于 1%Quality50 时仍控制在 5% 以内。如果 Quality50 就飙到 20% 以上优先检查嵌入时是否往 cA 加而不是往 cH 加——高频子带在压缩下几乎会被清零这是最常见的原因。5. 水印参数表与两个容易忽略的细节最后一组内容直接给参数调优表和两个实战细节。要调的参数不复杂但几项指标是相互牵制的动一个就要观察其他两个。参数建议范围对鲁棒性的影响对不可见性的影响嵌入强度 alpha0.02~0.05越大提取率越高越小越隐蔽分解层数1~3层数高抗缩放更强低层改动更温和小波基haar/sym4/db2sym4 在高频表现更平滑haar 块效应更明显嵌入通道G 或 YY 通道抗压缩稍好G 通道色偏更小水印尺寸≤ cA 面积的 1/4越小冗余越高与尺寸无关第一个容易被忽略的细节是水印置乱。直接按原样嵌入时裁剪攻击只要切掉水印所在区域整段信息就没了。嵌入前用 Arnold 置乱把水印像素均匀铺开提取后逆置乱复原。Arnold 猫映射的坐标迭代公式为[i; j] [1 1; 1 2] * [i-1; j-1] [1; 1]将所有坐标对图像阶数取模循环若干次后水印看起来就是一片均匀噪声。置乱后嵌入 cA 上不会形成规则图案攻击者即使定位到子带区域也无法直接读出有效信息。第二个细节是提取端的尺寸同步判断。水印提取出来之后常发现尺寸对不上原因在于dwt2对 512×512 输入输出 256×256 的 cA水印若本身是 64×64 就需放大四倍嵌入。放大后wm_bin与原始水印比对时直接按像素比较会因块状边界错位产生大量误检。正确做法是用nearest插值把原始水印放大到 cA 尺寸再评估或更精细一点以 4×4 像素块为单位做多数判决这样误码率能明显下降。最后给一个可直接用于验收的指标组合嵌入端输出 PSNR提取端输出 BER 和 NC。PSNR 低于 38 dB 肉眼可见痕迹NC 高于 0.9 具备实际取证价值BER 低于 5% 可认为攻击后水印依然有效。把原始图、含水印图、提取水印和攻击对比曲线整理进课题报告配上预处理和攻击函数的调用代码整个闭环就能直接交稿。本文还有配套的精品资源点击获取