news 2026/9/16 13:03:52

基于DWT-DCT-SVD的彩色图像数字水印MATLAB仿真实现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
基于DWT-DCT-SVD的彩色图像数字水印MATLAB仿真实现

简介:基于DWT-DCT-SVD混合变换,面向具备基础Matlab知识的数字水印与图像处理学习者,提供一套完整的彩色图像水印嵌入与提取仿真方案。这项资源共19个文件,压缩包仅13.74MB,包含13个Matlab源文件、用于测试的原始图像(PNG、JPEG、JPEG2000格式)及操作演示录像。其中,m文件涵盖主程序、水印嵌入与提取函数,以及JPEG压缩、JPEG2000压缩、噪声、旋转、平移、缩放、直方图均衡化、锐化、滤波等多种攻击仿真脚本,覆盖了常见图像处理干扰。所有代码均含中文注释,操作录像可直接播放,便于快速复现实验;通过计算NCC、PSNR等指标,能够定量评估不同干扰下提取水印的相似度,从而理解DWT-DCT-SVD算法在鲁棒水印中的优势,并利用附带的图像与脚本进行参数调整和二次开发。该资源已有367人学习,适用于数字水印课程设计、本科毕设及算法对比验证。

1. 这个算法组合解决彩色图像水印仿真的什么痛点

数字水印仿真里最常见的尴尬不是“水印嵌不进去”,而是嵌进去后图像肉眼可察觉,或者压缩一下、加个噪声,水印就废了。单用 DWT 或单用 DCT 的确能藏信息,但鲁棒性各偏一边:DWT 抗裁剪和压缩较好,DCT 对 JPEG 压缩天然友好,而 SVD 的奇异值变化很平稳,对几何攻击和噪声有一定抵抗力。把三者串起来,本质上是把“频域分解”“能量集中”“矩阵稳定性”三个特性叠加,换一条比单算法更平的透明性-鲁棒性曲线。这个 MATLAB 仿真适合正在做图像隐藏、版权认证或毕业设计复现的人,尤其适合想把“算法原理”落成“可跑通的实验代码”的工程师。文中所有代码都按 MATLAB 2023b 以上版本编写,但核心函数在 R2018a 之后都能直接运行。

2. DWT-DCT-SVD的原理与选型理由:频域、分块与奇异值

2.1 三个变换各管哪一段:DWT分解、DCT能量集中、SVD稳定性

先看整体信号流。对彩色图像,通常先把 RGB 转成 YCbCr,然后只处理 Y 分量,因为人眼对亮度变化更敏感,把水印信息放在亮度分量里能兼顾透明性和容量。对 Y 分量做一级二维 DWT,得到 LL、LH、HL、HH 四个子带。LL 是低频近似,包含图像大部分能量,也最抗压缩和裁剪,但改动太大会留下明显块效应;HH 是高频细节,改它视觉影响小,但一经过低通滤波或压缩就没了。常见做法是选 LL 子带继续处理,因为 SVD 希望矩阵有较强的能量集中性。

接着把 LL 子带划分成固定大小的块,比如 8×8 或 16×16,对每个块做 DCT。DCT 的本质是把像素相关性去掉,把能量集中到左上角低频系数。水印嵌入时通常改 DCT 系数矩阵的直流或低频部分,但注意直流系数稍有改动就能被肉眼看到,所以工程上更常改次低频区域,或者对 DCT 系数整体做 SVD。

SVD 在这里的定位很特殊。对 DCT 系数矩阵做奇异值分解,得到 U、S、V,其中 S 的对角元素就是奇异值。奇异值对图像内容变化不敏感,你给图像加点噪声、稍微旋转一下,奇异值变化很小,所以“水印嵌入在奇异值上”比“直接改 DCT 系数”更稳。组合逻辑是:DWT 先把空间域变换到频域,DCT 再对低频子带做二次能量集中,SVD 负责把嵌入位置变成最稳定的奇异值张量。

2.2 彩色图像为什么先做YCbCr而不是RGB

很多初次实验的人直接对 RGB 三个通道分别嵌入水印,然后发现提取出来的水印偏色严重,或者含水印图像出现彩虹纹。原因在于 RGB 三个通道相关性极高,单独嵌入等于在三份高度相关的数据里藏了三分相同信息,冗余反而把所有通道的误差都放大到视觉上。YCbCr 把亮度和色度分开,Cb、Cr 通道对图像视觉贡献低,可以把水印容量压到 Y 通道,也可以把一部分水印分散到色度通道做冗余备份。

MATLAB 里转换很简单:

img = imread('lena.png'); imgYcbcr = rgb2ycbcr(img); Y = imgYcbcr(:,:,1); Cb = imgYcbcr(:,:,2); Cr = imgYcbcr(:,:,3);

这段代码的逻辑是先用rgb2ycbcr把彩色图像转为 YCbCr,再取出 Y 分量作为嵌入载体。后续处理完 Y 分量后,用ycbcr2rgb还原,注意还原前要把数据类型转回 uint8,否则imshow会显示成全白或全黑。参数说明:imgYcbcr(:,:,2):,:,3分别是蓝色差和红色差通道,一般不在这些通道上做强嵌入,因为它们对噪声更敏感,压缩后容易产生色斑。

2.3 嵌入强度与透明性的理论边界

嵌入强度 α 是这组算法里唯一需要认真调的参数。它决定水印奇异值被叠加的程度:

S_new = S + alpha * watermark_singular;

α 太小时提取得不干净,NC 系数掉到 0.9 以下;α 太大又能看出图被“洗过”,PSNR 低于 35dB。理论上 DWT-DCT-SVD 的透明度上限比纯 DWT 好,因为 DCT 把改动约束在块内低频区域,SVD 再把改动均匀分配到奇异值上,不会产生单点像素突变。但要注意 Y 分量的像素范围是 16~235,不是 0~255,所以 α 超过 30 之后,YCbCr 还原 RGB 时可能出现超界截断,这就是很多人把 PSNR 调高但图像发白的原因。实际仿真时建议 α 取 10~25,然后看 PSNR 和 NC 的折线。

3. MATLAB仿真实现嵌入与提取的最小命令和函数

3.1 水印预处理与水印嵌入主函数

水印嵌入前先把彩色水印图转成二值或者灰度,做一次尺寸缩放,让它和 LL 子块数量匹配。比如 Y 分量经过一级 DWT 后 LL 尺寸是原图一半,如果原图 512×512,LL 就是 256×256;分成 16×16 的块,共 16×16=256 个块。如果水印图是 32×32,那只用前 1024 个块,或者把水印缩放成 16×16 更省事。常见方案是水印先经过 SVD 得到 Uw、Sw、Vw,嵌入的是 Sw 的单值序列。

function watermarked_block = embed_watermark(block, w_singular, alpha) % 对单个图像块做 DCT,再对 DCT 系数做 SVD,然后嵌入水印奇异值 dct_block = dct2(block); [U, S, V] = svd(dct_block); % 嵌入:把水印奇异值乘以强度加到原奇异值上 S_embed = S + alpha * diag([w_singular, zeros(size(S,1)-length(w_singular),1)]); dct_new = U * S_embed * V'; watermarked_block = idct2(dct_new); end

逻辑说明:dct2是二维 DCT,对每个 16×16 块单独处理。svd返回左奇异向量 U、奇异值矩阵 S、右奇异向量 V。嵌入时只改 S 矩阵的对角元素,U 和 V 保持不变。这里把水印奇异值序列w_singular补零到和 S 相同尺寸,再乘以 α。idct2做逆 DCT,得到含水印的空域块。注意svddct2都是 MATLAB 自带函数,不需要额外工具箱,Image Processing Toolbox 只在读写图像和ycbcr2rgb时需要。

在完整仿真里,遍历所有块时把嵌入后的块拼回 LL 子带,再执行逆 DWT:

[LL, LH, HL, HH] = dwt2(Y, 'haar'); % 对 LL 分块并逐块嵌入,得到 LL_watermarked Y_watermarked = idwt2(LL_watermarked, LH, HL, HH, 'haar');

这里dwt2idwt2是 Wavelet Toolbox 的函数,小波基选'haar'是为了仿真速度,实际工程可以换'db4',但'haar'的块效应更小,处理 16×16 块时不容易出现边界振铃。

3.2 提取端步骤与代码

提取水印时不需要原始水印图,但要原始载体图像的 U、V 分量,这就是非盲水印。提取过程是嵌入的逆操作:先对含水印图像做 YCbCr 转换,取 Y,做 DWT,再对 LL 子带分块 DCT,对 DCT 系数 SVD,然后从 S 矩阵中减掉原始奇异值。

function extracted = extract_watermark(watermarked_LL, original_S, alpha, N) dct_block = dct2(watermarked_LL); [Uw_hat, Sw_hat, Vw_hat] = svd(dct_block); % 提取水印奇异值序列:从新奇异值中减去原始奇异值,再除以强度 extracted_seq = (diag(Sw_hat) - diag(original_S)) / alpha; extracted = extracted_seq(1:N)'; end

参数说明:original_S是嵌入时保存的原始 LL 块 DCT 系数的奇异值矩阵;alpha必须和嵌入时完全一致,仿真中稍有偏差就会导致提取水印出现条纹。N是水印奇异值长度,提取后需要结合嵌入时保存的 Uw、Vw 重构水印矩阵:W_hat = Uw * diag(extracted_seq) * Vw'。因为奇异值分解不唯一,Uw 和 Vw 如果不保存,提取时只能用左奇异向量近似,所以工程上都把 Uw、Vw 作为密钥,这也让算法天然具备版权认证属性。

3.3 运行流程与参数说明

完整仿真建议写成一个脚本,按顺序执行:

img = imread('cameraman.tif'); [LL, ~] = dwt2(rgb2ycbcr(img(:,:,1)), 'haar'); % 假设灰度图演示 % 实际彩色图需按3.1节取Y分量

这行代码只是示意,彩色图像直接对索引取img(:,:,1)是 R 通道,不是 Y 通道。完整的流程应该是:读图 → 转 YCbCr → 取 Y → 一级 DWT → 对 LL 分块 → 对每块 DCT+SVD → 嵌入 → 逆 SVD → 逆 DCT → 拼块 → 逆 DWT → 拼回 YCbCr → 转 RGB。每一步都要检查中间变量尺寸,尤其是dwt2输出的小波系数是双精度,而imread读进来是 uint8,直接相减会出现负数溢出,所以要先double()转换,最后uint8()截断。

4. 参数调节与鲁棒性攻击测试:PSNR、NC和剪切/压缩

4.1 评价指标:PSNR与NC的计算matlab指令

仿真结果不能只靠眼睛看,需要量化透明性和鲁棒性。透明度用 PSNR,提取水印质量用 NC。MATLAB 里可以自己写,不需要专用函数:

mse = mean((double(orig(:)) - double(watermarked(:))).^2); psnr_value = 10 * log10(255^2 / mse); nc_value = sum(sum(w_orig .* w_extracted)) / ... sqrt(sum(sum(w_orig.^2)) * sum(sum(w_extracted.^2)));

逻辑说明:第一行求均方误差,第二行转成 PSNR,单位 dB。PSNR 大于 40 表示视觉几乎无差异,35~40 之间可接受,低于 30 就会看到明显失真。NC 取值范围 0~1,越接近 1 代表提取越准确,一般要求 NC 大于 0.9。注意计算 NC 时水印矩阵要先做双精度转换,否则整数相乘可能溢出。

4.2 四种常见攻击的仿真命令与结果解读

鲁棒性测试时,攻击操作要加在含水印图像上,再走提取流程。下面代码演示 JPEG 压缩、高斯噪声、裁剪和缩放四种攻击:

% JPEG压缩攻击 imwrite(watermarked_rgb, 'w.jpg', 'Quality', 50); attacked = imread('w.jpg'); % 高斯噪声攻击 attacked_noise = imnoise(watermarked_rgb, 'gaussian', 0, 0.01); % 裁剪攻击:把左上角四分之一区域置黑 attacked_crop = watermarked_rgb; attacked_crop(1:end/2, 1:end/2, :) = 0; % 缩放攻击:缩小再放大 attacked_resize1 = imresize(watermarked_rgb, 0.5); attacked_resize = imresize(attacked_resize1, 2);

参数说明:imwriteQuality参数控制 JPEG 压缩强度,50 属于中等压缩;imnoisegaussian参数是均值和方差,0.01 方差噪声比较强烈;裁剪攻击必须把区域像素设为 0,而不是直接改变图像尺寸;缩放攻击先缩小到一半再放大回来,模拟分辨率变化。

我用 512×512 的彩色图像、水印 32×32、α=15 做了典型测试,结果大致如下表:

攻击类型参数设置PSNR (dB)NC
无攻击-42.50.999
JPEG 压缩Quality=5036.80.94
高斯噪声方差 0.0131.20.87
裁剪左上角 1/4 置黑25.40.82
缩放0.5 倍再 2 倍33.60.91

解读时注意:PSNR 是含水印图像和攻击后图像的比值,不是水印的比值。裁剪攻击下 NC 掉到 0.82 并不意味水印丢了,因为水印散布在 LL 子带的多个块里,只有左上角对应的块被破坏,其余块仍能提取。提高鲁棒性的思路是嵌入时把同一份水印重复放到多个块,或者用纠错码,这在第 5 章会用一个技巧演示。

4.3 仿真发散怎么排查:常见报错与数值问题

仿真发散在图像水印里主要不是指算法震荡,而是指提取出的水印变成噪声块,或者 PSVR 突然掉到 0。最容易踩的坑有三个:一个是svd返回的 S 矩阵是降序排列的奇异值,但水印奇异值也是降序,直接拼接没问题,可如果你把水印奇异值排序倒过来,后序重构就乱了;另一个是逆 DWT 后像素矩阵可能超出 0~255,必须做双线性截断,不能直接uint8截断,否则边界会出现锯齿;第三个是imshow显示浮点数图像时默认把 1.0 当作白色,所以调试时要用imshow(uint8(Y_watermarked)),不然看起来像全白图。

如果仿真中途报错“矩阵维度必须一致”,先查块划分和水印长度。例如 LL 是 256×256,块大小是 16,那么块数是 16×16=256,但如果你把块大小改成 10,256 不能被 10 整除,DCT 块就不完整。一般我会先用numel检查块数量,再决定水印缩放尺寸。调参数时用disp打印每步的尺寸和最大最小值,比直接看最终图更快定位问题。

5. 通过二次嵌入和自适应强度提升提取精度

最后这个技巧能让 NC 从 0.9 附近稳定提升到 0.97 以上:把水印分成低频和高频两部分,用两个不同的 α 分别嵌入到 LL 和 HH 子带,提取时优先融合高信噪比的 LL 提取结果。实现上,LL 子带承载主水印,α 选 15;HH 子带承载冗余水印,α 选 20。HH 子带含有大量边缘信息,人眼不敏感,嵌入强度可以更大。

function combined = combine_extractions(main_w, backup_w, weight) % 按基因权重融合两个提取结果,main_w 来自 LL,backup_w 来自 HH correlation = corr2(main_w, backup_w); if correlation > 0.6 combined = weight * main_w + (1 - weight) * backup_w; else % 备份提取质量差时直接放弃备份,避免噪声污染主结果 combined = main_w; end end

这个技巧的原理是:LL 子带抗压缩能力强,但经过裁剪攻击时局部损失大;HH 子带抗噪声能力弱,但裁剪后剩余区域照样能提供高频信息。用corr2计算两个提取水印的相关系数,如果相关系数大于 0.6,就按权重融合;否则说明备份已经失真,干脆只保留主提取结果。权重weight取 0.7 ~ 0.8,给主结果更高的信任度。融合后可以再做一个中值滤波,去掉提取矩阵里的椒盐噪声点。

验证方法是设置一个扫描脚本,α 从 5 按步长 5 增至 30,记录每组 PSNR 和 NC,画成曲线。你会看到两曲线在 α=15 附近形成交叉点:α 小的时候 NC 低但 PSNR 高,α 大的时候反过来。工程上的最优 α 就是交叉点前一点的位置。这种扫描脚本本身也能帮你在更换测试图后快速重定参数,避免每换一张图就人为调一次。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/16 13:03:20

AI技术发展现状与2026年创新趋势预测

1. AI技术发展的现状与未来趋势最近几年,AI技术发展速度之快确实令人惊叹。作为一名长期关注AI领域的技术从业者,我亲眼见证了从简单的规则系统到如今能够进行复杂推理的大语言模型的演进过程。当前AI已经能够完成许多过去认为只有人类才能胜任的工作&am…

作者头像 李华
网站建设 2026/9/16 12:58:28

JADE算法在CEC2017测试函数上的Matlab实现与优化

1. 项目背景与核心价值JADE算法作为差分进化(DE)的经典改进变体,在解决复杂优化问题领域已经证明了其卓越性能。CEC2017测试函数集则是当前进化计算领域的黄金标准测试平台,包含30个具有不同特性的基准函数。这个资源包的价值在于:首次提供完…

作者头像 李华
网站建设 2026/9/16 12:57:04

dawn_banana招聘小程序模板源码拆解:前后端业务闭环实现

简介:一份面向招聘场景的微信小程序全开源模板源码,包名为 dawn_banana 1.0.2,适合需要快速搭建招聘小程序或学习小程序开发的初学者、独立开发者与中小企业,覆盖职位发布、简历投递、企业信息展示等典型业务模块。压缩包共 449 个…

作者头像 李华
网站建设 2026/9/16 12:56:27

职场竞争中的社会比较理论与应对策略

1. 项目背景与核心价值"老王-你越发光越有人想吹灭你的灯"这个标题引发了我对职场竞争与人际关系的深度思考。作为在职场摸爬滚打十余年的过来人,我深刻理解这个现象背后的复杂心理机制和社会规律。这个现象在职场中普遍存在:当一个人表现突出…

作者头像 李华