ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

DWT数字水印MATLAB GUI实现:原理、代码与抗攻击验证

DWT数字水印MATLAB GUI实现:原理、代码与抗攻击验证 简介基于MATLAB的DWT数字水印设计程序配套完整GUI界面主要面向计算机、通信、人工智能、自动化等专业学生及从业者适用于课程设计、课程大作业或毕业设计等场景也可作为数字水印算法入门与进阶的参考项目。压缩包共11个文件4个.m脚本覆盖DWT变换、水印嵌入/提取及Arnold置乱与周期计算GUI.fig提供可视化操作界面2张bmp分别作为载体与水印样例data、p、db及doc指导文档辅助运行和快速上手整体仅523KB结构紧凑、便于部署。目前已有49人学习下载。项目实现了基于离散小波变换的数字水印嵌入与提取全流程用户可在界面上直观完成参数设置与结果预览代码层次清晰既适合初学者理解算法原理也支持在此基础上调整算法参数、加入攻击测试或扩展更多功能学习与借鉴价值较高。1. 为什么这个DWT数字水印项目值得你拆开看数字水印的课程设计最常见的做法是在空域做LSB替换代码短能跑但图像一经过JPEG压缩或加噪水印立刻消失。而基于DWT离散小波变换的方案是把水印嵌入到小波分解后的低频系数中抗压缩和抗噪声能力强得多。这个项目的好处在于它把完整的DWT嵌入提取流程封装成了MATLAB GUI打开界面选图点按钮就能看到载体、水印、含水印图像和提取结果全程不用敲命令行。正在做课程设计、大作业或者想快速验证DWT水印效果的读者可以拿来直接跑通再逐行拆。就算你原封不动运行也能在GUI回调里看清函数调用关系想进阶可以改嵌入强度、换载体类型、或者自己加攻击测试。2. DWT数字水印的原理与项目文件结构2.1 从空域到小波域水印该藏在哪里空域水印直接修改像素值比如把最低有效位替换成水印位优点是实现简单坏处是鲁棒性差JPEG压缩、几何裁剪都会把最低位的信息抹掉。频域水印的思路是先把图像变换到频域再把水印能量叠加到某个频带系数上。DWT之所以在数字水印里用得频繁是因为它同时保留空间位置和频率特征图像经过一次小波分解后会得到LL、LH、HL、HH四个子带。LL是低频近似集中了图像大部分能量人眼对低频区域的变化相对敏感所以嵌入强度不能太大但低频系数在压缩和滤波时不容易被丢弃这就是DWT水印鲁棒性的来源。实际项目中一般选用Haar小波或者db1小波因为计算简单、程序好写。这个项目里具体用了哪种小波基要看DWT_Watermarking.m里的调用参数但绝大多数课程设计版本用的是Haar小波。理解这一点是后续改参数的前提换小波基会影响分解系数分布直接改变提取效果。2.2 项目文件清单与启动步骤拿到压缩包解压后先别急着双击运行按下表的对应关系把文件理清楚文件名作用需要自己改吗GUI.m / GUI.figGUIDE生成的界面与回调函数改界面布局时才动DWT_Watermarking.mDWT水印嵌入与提取的主函数调算法参数时看这里Arnold1.m水印图像Arnold置乱实现一般不用改ArnoldPeriod.m计算当前水印尺寸对应的Arnold周期换水印图时要关注载体图像.bmp测试用原始载体可替换成自己的灰度图水印.bmp测试用二值水印可替换成自己的水印如何导入程序.doc作者的导入说明文档按文档操作即可Unt.p / toolbox.data附加数据或加密中间文件一般忽略启动步骤在MATLAB里很简单。把解压后的文件夹设成当前目录然后在命令行执行cd(你的路径\DWT_Demo); GUI;这里不建议双击GUI.m运行因为GUIDE生成的函数如果当前工作目录不对加载图像时容易出现找不到相对路径的问题。用cd切换到项目目录后再执行GUI命令最稳。需要注意这里的GUI是文件名同时也是GUIDE自动生成的函数名执行后MATLAB会打开图形界面之后点击按钮就会触发对应回调函数。2.3 水印嵌入与提取的整体流程理解这个项目的流程比背代码更重要。嵌入过程可以拆成五步读入载体图像如果是彩色图先转灰度图因为DWT处理的是二维矩阵读入水印图一般要求是二值图尺寸建议小于等于载体图像的子带尺寸对水印做Arnold置乱把像素位置打乱这样即使嵌入区域被局部裁剪水印信息仍然散布在全图对灰度图像做一层或多层DWT分解提取LL低频子带再把置乱后的水印按一定强度叠加到LL系数上做逆小波变换还原图像保存含水印图像。提取流程是嵌入的逆过程读入含水印图做同样的DWT分解得到LL子带然后从LL中分离出置乱水印再按相同次数做Arnold反置乱得到提取出的水印。这个项目的DWT_Watermarking.m很可能同时包含嵌入和提取两个函数也可能是通过一个flag参数区分。用GUI操作时就直观很多点击“嵌入水印”后图像区会分别显示载体、水印和含水印图像。流程清楚了后面调试起来就能快速定位是哪个环节出了问题图像读入、小波分解、系数叠加、还是反变换保存。3. Arnold置乱与DWT系数嵌入的MATLAB实现3.1 Arnold变换原理与周期计算Arnold置乱也叫猫脸变换它把图像中坐标为(x,y)的像素映射到新位置(x,y)公式是x (x y) mod Ny (x 2y) mod Nx和y从0开始计数N是图像边长。这个变换是周期性的迭代若干次后图像会恢复原样这个迭代次数叫Arnold周期。项目里的Arnold1.m就是做这个映射ArnoldPeriod.m用来计算当前水印边长对应的周期目的是让提取时知道需要反置乱多少次。Arnold置乱的代码通常这样写function out Arnold1(img, times) n size(img, 1); tmp zeros(n, n); for k 1:times for i 1:n for j 1:n ni mod((i-1) (j-1), n) 1; nj mod((i-1) 2*(j-1), n) 1; tmp(ni, nj) img(i, j); end end img tmp; end out img; end注意上面代码里坐标从1开始所以用i-1和j-1代入公式再补回1这是最容易写错的地方。times是置乱次数由外部传入对应嵌入时选用了几次置乱。提取时可以用两种方式还原一是直接对提取出的水印再做times次Arnold变换利用周期性复原另一种是用ArnoldPeriod算出周期P再做(P - times)次变换。后者更直观也方便在GUI里给用户提示当前置乱次数是否超过周期。周期计算的典型实现是对单位矩阵反复做Arnold变换直到矩阵又变回自身循环次数就是周期function p ArnoldPeriod(n) A eye(n); B Arnold1(A, 1); p 1; while ~isequal(B, A) B Arnold1(B, 1); p p 1; end end这里n是水印边长。这个双重循环的时间复杂度是O(n² × times)如果水印是128×128置乱10次大约16万次循环MATLAB还能接受换到256×256时就会明显变慢建议把Arnold1里的坐标映射改写成向量化操作。3.2 小波分解与嵌入强度的选择嵌入阶段的核心在DWT_Watermarking.m。常见做法是用dwt2做单层分解得到四个系数矩阵% 只读灰度图并转成double避免uint8溢出 I_gray im2double(I); [LL, LH, HL, HH] dwt2(I_gray, haar); % 把二值水印统一成0/1矩阵并缩放到LL子带尺寸 W imresize(W, size(LL)); W_bin W 0.5; % 嵌入强度alpha越大越鲁棒但越影响画质 alpha 0.05; LL_w LL alpha * W_bin; % 逆变换得到含水印图像 I_w idwt2(LL_w, LH, HL, HH, haar);这里选择把水印嵌入LL低频子带而不是HH高频子带因为高频系数经过JPEG压缩后会被大量滤除。alpha是嵌入强度是一个标量项目GUI里通常做成可输入项。alpha太小提取时水印会被周围系数干扰alpha太大图像会出现明显块效应和模糊。经验上在0.01到0.1之间调整。除了大小数据类型也要注意没有用im2double直接对uint8图像累加会因为溢出产生白斑所以务必先统一数据格式。另外水印图读进来可能是uint8的0和255也可能已经是logical型。上面代码中W 0.5的作用是把0/255的二值图归一化成0/1数值这样乘以alpha之后叠加量才是alpha级别否则会一下子加上255倍强度图像直接花掉。3.3 水印提取与反置乱提取时有两种路线。一种是盲提取只凭含水印图像和已知的嵌入规则恢复水印另一种是非盲提取用原始载体图像参与计算。课程设计里为了展示清晰很多直接在提取时调用原始载体做差分。典型代码如下% 读取原始载体和含水印图像的LL系数 [LL_o, ~, ~, ~] dwt2(orig_gray, haar); [LL_w, ~, ~, ~] dwt2(watermarked_gray, haar); % 差分并还原水印 diff_LL LL_w - LL_o; W_ext diff_LL / alpha; W_ext W_ext 0.5; % 二值化这段代码的逻辑是嵌入时加了alpha*W_bin提取时用含水印LL减去原始LL再做除法就还原出水印系数最后二值化。非盲提取适合算法验证和测试鲁棒性不适用于真正的版权认证。如果项目GUI里没有要求用户重复上传原始载体那它很可能实现了盲提取一般是基于量化索引或系数相关性。阅读DWT_Watermarking.m时重点看它的函数参数如果只传入了水印图像和一个图那就是盲提取如果同时传入了载体和水印图大概率是非盲提取。3.4 实现中的常见误用很多人在写DWT水印时会把水印放进HL或HH子带理由是高频细节人眼不敏感。但在JPEG压缩和低通滤波攻击下高频系数被破坏得最严重。嵌入LL虽然可见度略高但鲁棒性明显更好这是课程设计和论文里最容易踩的选型坑。另一个常见错误是水印尺寸与子带不匹配。载体是512×512一层小波后LL是256×256水印就必须缩放成256×256或者采用分块嵌入。直接用原尺寸相加MATLAB会报“矩阵维度不一致”。比较稳妥的做法是利用imresize把水印缩放成LL的大小或者用重复铺满的方式填满子带。Arnold置乱的次数还需要保证嵌入和提取一致。嵌入时置乱20次提取时只反置乱19次提取出的水印是乱的。建议在GUI里把置乱次数做成可输入项并在旁边提示当前水印尺寸的Arnold周期上限。4. MATLAB GUI交互把算法封装成可操作界面4.1 GUI控件布局与职责划分打开GUI.fig后会看到一个典型的GUIDE布局。界面里的控件基本可以分成四类文件选择、参数输入、功能按钮、图像显示。以常见课设版本为例各控件映射关系如下控件类型Tag名示例职责Axesaxes_cover显示原始载体图像Axesaxes_watermark显示原始水印或提取结果Axesaxes_result显示嵌入后的含水印图像Editedit_alpha输入嵌入强度alphaEditedit_times输入Arnold置乱次数Push Buttonbtn_load_cover打开文件选择框读取载体Push Buttonbtn_embed执行水印嵌入Push Buttonbtn_extract执行水印提取Texttext_status显示运行状态和PSNR等信息这个表不一定和你的资源里Tag完全一致但功能对应关系类似。在MATLAB里双击GUI.fig中的控件可以在属性检查器里看到具体的Tag和Callback方便对照代码。4.2 关键回调函数的写法回调函数是GUI.m里的子函数按钮点击时触发。嵌入按钮的回调一般这样组织function btn_embed_Callback(hObject, eventdata, handles) % 从句柄里取已经读入的图像 cover_img getappdata(handles.figure1, cover_img); wm_img getappdata(handles.figure1, wm_img); if isempty(cover_img) || isempty(wm_img) msgbox(请先加载载体图像和水印图像, 提示); return; end alpha str2double(get(handles.edit_alpha, String)); times str2double(get(handles.edit_times, String)); % 调用主函数执行嵌入 watermarked DWT_Watermarking(cover_img, wm_img, alpha, times); % 显示结果并保存到句柄 axes(handles.axes_result); imshow(watermarked); setappdata(handles.figure1, watermarked_img, watermarked); end这个回调用getappdata/setappdata存取图像比全局变量清晰也是GUIDE项目常见的做法。handles是GUIDE自动传递的结构体保存了所有控件的句柄。get(handles.edit_alpha,String)获取的是字符串必须用str2double转成数值否则后面矩阵运算会报错。成功嵌入后把含水印图像存到appdata这样提取按钮可以直接使用不需要再重新读图。4.3 参数输入与容错处理参数输入是GUI里最容易被用户敲坏的地方。比如把alpha输入成“abc”或者留空str2double会返回NaN后续代码全崩。所以加一段校验逻辑是基本操作alpha str2double(get(handles.edit_alpha, String)); if isnan(alpha) || alpha 0 || alpha 1 msgbox(alpha必须介于0到1之间, 参数错误); return; end times str2double(get(handles.edit_times, String)); if isnan(times) || times 1 msgbox(置乱次数必须为正整数, 参数错误); return; end times round(times);round是因为用户可能输入“3.5”而置乱次数只能是整数。把这些校验放在回调函数最前面比在算法内部报错更容易排查。GUI另一个高频问题是从uigetfile选图时返回的全路径为空用户取消选择后代码仍继续执行所以回调里一定要先判断返回结果是否为空再执行后续处理。4.4 GUIDE与App Designer兼容性注意这个项目基于GUIDE框架生成文件是GUI.fig和GUI.m。GUIDE在R2016a之后不再增加新功能但仍然可用到R2020a之后MATLAB官方更推荐App Designer不过课设提交场景下GUIDE依然能正常打开运行。如果你用的是MATLAB R2023b或R2024a打开fig时界面可能提示“GUIDE将在未来版本中被移除”点继续打开即可。项目里的Unt.p是一个加密的p文件从名字看可能是某段核心函数的编译版本。p文件能运行但不能查看源码。如果你的MATLAB版本与打包版本不一致可能出现“无效的P文件”错误解决方法是找作者用你的版本重新导出或者干脆自己实现一个同名函数替换掉它。课设答辩时老师常会问这里提前想清楚这个文件在代码里到底被谁调用会加分不少。5. 从结果验证到抗攻击测试NC/PSNR怎么算5.1 两个客观指标的计算代码嵌入效果不能只靠眼睛看通常用PSNR衡量图像失真用NC衡量水印提取相似度。PSNR高于30dB表示画质可接受NC接近1表示水印提取成功。计算代码可以这样写orig double(imread(载体图像.bmp)); wat double(imread(含水印图像.bmp)); if size(orig,3)3 orig rgb2gray(uint8(orig)); end if size(wat,3)3 wat rgb2gray(uint8(wat)); end mse mean((orig(:)-wat(:)).^2); psnr 10*log10(255^2 / mse); nc sum(orig(:).*wat(:)) / sqrt(sum(orig(:).^2)*sum(wat(:).^2));输入图像需要先统一灰度和数据类型否则mse和nc没有意义。如果图像是0到1的double计算PSNR时要把255换成1。NC的公式在不同论文里写法略有差异但用这个余弦相似度版在课设里足够说明问题。5.2 对含水印图像做常见攻击并观察NC验证鲁棒性就要对含水印图像做攻击后再提取水印并计算NC。常见的攻击模拟代码如下% JPEG压缩攻击 imwrite(uint8(wat), attack.jpg, Quality, 50); atk imread(attack.jpg); % 高斯噪声攻击 atk2 imnoise(uint8(wat), gaussian, 0, 0.01); % 裁剪攻击 atk3 uint8(wat); atk3(1:128, 1:128) 0;对攻击后的图像分别调用提取函数得到提取水印后计算NC记录下来对比。这样可以直观看到alpha、置乱次数对鲁棒性的影响alpha大时抗噪声更强但PSNR会下降置乱次数提高后抗裁剪能力会增强因为水印信息被打散到全图各个位置。5.3 常见报错处理跑这个项目最常见的三个报错一是“未定义函数或变量DWT_Watermarking”多半是当前目录没切到项目文件夹或者该文件被MATLAB路径排除二是“图像数据尺寸不一致”基本是彩色图没转灰度或者水印没有缩放到子带大小三是“输入参数不足”通常是直接运行命令时没有按GUIDE方式提供handles改成直接点UI控件触发回调就好。最后一个小技巧如果项目里的Unt.p让你不放心你可以在理解DWT嵌入算法后自己写一个同名函数替换掉它然后把GUI回调指向你的新函数。这样既卸掉了加密文件的包袱也能在答辩时清楚讲出每一步的实现细节。matlab gui设计本身只是壳最终的评分点还是你对DWT水印原理的解释和参数调试能力。本文还有配套的精品资源点击获取
返回列表