简介:这是一份面向音频水印与信息隐藏方向的MATLAB实现资源,核心解决将音频信号通过单稳态DCT、DWT及anord方法嵌入二值图像,并能准确提取的问题,适合信号处理、多媒体安全方向的学生、研究者与工程师参考。压缩包共14个文件、约20.61MB,包含4个.m算法脚本(如DWT_DCT.m、arnold.m、iarnold.m)、3个wav与1个mp3音频样本、2个jpg测试图像、2个md说明文档及配置文件,目录结构清晰,便于按模块阅读和二次开发。目前已有174人学习使用。资源提供了完整的嵌入与提取流程代码、多种音频样例与基础实验图像,并附带README说明,可帮助读者快速复现音频隐藏实验;通过运行代码可观察DCT/DWT系数修改、arnold置乱对鲁棒性的影响,并利用信噪比、峰值信噪比等评价指标量化嵌入效果;还可调整嵌入强度或选择不同宿主图像,对比提取音频的失真变化,为深入理解信息隐藏算法提供了可操作的实验平台。
1. 音频嵌入与提取:为什么 DWT、DCT 与 ANORD 要放进同一个工程
很多人做音频嵌入时只挑一个变换域下手,等真正丢到重采样、MP3 压缩或叠加噪声的真实信道里再提取,才发现单一变换的系数经不住扰动。标题里的 ANORD 指自适应噪声鲁棒检测,它与 DWT、DCT 配合的完整流水线是:DWT 把时域信号按多分辨率拆到不同频带,DCT 再对选定子带做能量压缩,ANORD 则负责在带噪系数中做带判决的比特恢复。这个组合能同时拿到时频分辨率和能量聚拢能力,适合在 MATLAB 里快速跑通并验证鲁棒性。下面按嵌入端、提取端、参数调整三个层次展开,新手可以照着复现,熟手可以直接对照自己的选型参数。
2. DWT 与 DCT 的分工:ANORD 需要双域特征的三个理由
2.1 DWT 逐层分离时频能量
离散小波变换把一帧音频逐级分解成近似系数和多层细节系数,近似系数保留低频骨架,细节系数对应边缘与瞬态。对语音和音乐而言,人耳对低频不敏感的部分不多,直接改细节系数容易被听出来;改太深层的近似系数又会让宿主信号整体漂移。常见的做法是取三层小波分解后的近似子带,也就是把一帧 4096 点信号经过三次抽取变成 512 点低频系数,再对这个数组做 DCT。这样的好处是低频近似已经经过低通滤波,DCT 后能量会非常集中,嵌入一个小扰动就能在提取端产生可辨识的奇偶变化。
小波基的选择同样影响提取稳定性。我一般用db4,它的紧支撑特性能兼顾时域定位和频域平滑,haar虽然计算最快,但系数连续性差,音频重采样后 DCT 系数容易跳格。分解层数则受帧长约束,4096 点做四层分解后近似系数只剩 256 点,此时选择嵌入系数的范围就变窄了。层数越高,单点受攻击时扩散范围越大,这不见得是坏事,反而能把局部失真摊开到更多系数上,所以三层到四层是常用区间。
2.2 DCT 系数排序与能量压缩特性
DCT 把实数序列变成实数谱,能量集中在低频索引附近,这给量化嵌入提供了明确的位置语义。DCT 系数数组中第 1 个是直流分量,绝对不能动,动一点点整帧幅度就跳变。第 2 到第 30 个系数覆盖中低频段,是嵌入水印的主流区间;再往后的高频系数数值小,量化步长稍大就会破坏其统计形态,提取时也容易被滤波或压缩抹掉。
DWT 和 DCT 在这一步形成明确分工:小波负责把信号按频率分层,DCT 负责在某一层内部重新分配能量。单做 DCT 的局限是没有尺度信息,攻击者只要做一次低通滤波,嵌入在高频 DCT 系数里的比特就没了;单做 DWT 的局限是细节子带内点与点之间相关性差,量化误差不容易控制。两者串联后,嵌入位置落在低频近似子带的 DCT 中频区,既有尺度上的稳定性,又有频域上的抗压缩能力。
| 对比维度 | 单独 DWT | 单独 DCT | DWT + DCT |
|---|---|---|---|
| 频率尺度信息 | 强,多分辨率 | 无,单层谱 | 强 |
| 能量集中程度 | 一般 | 很强 | 两层集中 |
| 中频嵌入位置 | 不直接 | 可直接定位 | 可定位且稳定 |
| 对重采样的抗性 | 中 | 低 | 高 |
| MATLAB 实现复杂度 | 中 | 低 | 略高 |
2.3 嵌入端和提取端 ANORD 的输入特征
ANORD 的输入不是原始音频,而是完成 DWT 和 DCT 之后的系数块。嵌入端把同一比特重复到多个 DCT 系数上,形成一个带冗余的奇偶模式;ANORD 从当前帧中估出噪声能量,并据此决定哪些系数参与最终投票。一个实用的噪声估计方法是统计 DCT 高频系数的绝对中位差,用它近似噪声标准差,然后设置一个判决门槛:
% 噪声估计与可用系数筛选 coeff_high = C(40:min(120, length(C))); % 高频区 sigma_n = 1.4826 * median(abs(coeff_high)); % 稳健标准差 use_idx = idx(abs(C(idx)) > 3 * sigma_n); % 只保留显著系数这段代码先取 DCT 高频段做噪声底估算,再筛掉低于 3 倍噪声标准的系数。ANORD 的自适应就体现在这里:同一帧在不同信噪比条件下,参与投票的系数数量会自动变化。信噪比高时保留更多系数,秩次更稳;信噪比低时只留下最大系数,避免把小扰动误判成比特。
3. 在 MATLAB 中运行 DWT、DCT 音频嵌入的最小工程
3.1 帧长、小波基、量化步长的准备
嵌入前先定义全局参数。帧长直接影响比特率和鲁棒性,4096 点对应 44.1 kHz 采样率下大约 93 ms,一秒钟能嵌约 10.7 bit。如果想提高容量,可以把帧降到 2048,但每帧 DCT 系数数量减半,中频嵌入区间要跟着收窄。初始化参数时我会把它们集中放进一个结构体,方便后面的嵌入和提取函数共用。
% 初始化参数 opts.frameLen = 4096; % 每比特对应样本数 opts.level = 3; % DWT 分解层数 opts.wname = 'db4'; % 小波基 opts.delta = 0.08; % 量化步长,越大越鲁棒但可听噪声越大 opts.idx = 4:23; % DCT 系数索引范围,跳过直流 opts.fs = 44100; % 音频采样率这里的delta是量化步长的核心,所有 DCT 系数都会被映射到以delta为间距的网格上。idx的范围决定了每帧承载多少冗余投票,20 个系数表示一个比特重复 20 次,抗抖动能力强很多。索引从 4 开始是为了避开直流和前两个极端低频系数,它们承载了整帧的基频能量,改动影响最明显。
3.2 嵌入函数 embed_audio_dwt_dct.m
下面是最小可运行版本。它把一帧音频做三层 DWT,取近似子带做 DCT,再用量化奇偶调制把比特写进选定系数,最后反变换回时域。
function stego = embed_audio_dwt_dct(audio, bits, opts) % audio: 单声道行向量 % bits: 0/1 序列,长度需与帧数匹配 frameLen = opts.frameLen; nFrames = min(floor(length(audio)/frameLen), length(bits)); stego = audio(1:nFrames*frameLen); for k = 1:nFrames frame = audio((k-1)*frameLen+1 : k*frameLen); b = bits(k); % 多级 DWT,保留各级细节用于反变换 ca = frame; detC = cell(opts.level, 1); for j = 1:opts.level [ca, detC{j}] = dwt(ca, opts.wname); end % 对近似子带做 DCT C = dct(ca); % QIM 奇偶量化嵌入 target = C(opts.idx); q = round(target / opts.delta); % 找最近量化格点 qbit = mod(q, 2); % 当前格点奇偶性 flip = qbit ~= b; % 奇偶不匹配则翻到下一格 q(flip) = q(flip) + 1; C(opts.idx) = q * opts.delta; % 写回量化值 % 反变换还原时域 caNew = idct(C); for j = opts.level:-1:1 caNew = idwt(caNew, zeros(size(detC{j})), opts.wname); end stego((k-1)*frameLen+1 : k*frameLen) = caNew(1:frameLen); end end代码逻辑分三段看。第一段做多级 DWT,每次分解保留细节系数,近似系数继续进下一层,这样反变换时能通过把细节系数置零完整还原出原长度的时域信号。第二段是嵌入核心,round先把目标系数对齐到最接近的量化格点,然后检查奇偶性是否和当前比特一致,不一致就把量化格点加一,这样既保证落在网格上,又让修改量不超过一个步长。第三段从最深层往上递归重构,每次idwt后长度翻倍,最后截断到原始帧长。
参数最需要留意的是delta与idx之间的搭配。delta太大,量化误差超过听觉掩蔽阈值,背景会冒出“沙沙”声;delta太小,提取端稍微经过一次滤波,系数就可能漂移出原来的格点。idx的范围也不能太靠前,前几个 DCT 系数数值大,round(target / delta)后翻格子的幅度相对大,听感变化明显。
3.3 嵌入后信噪比的快速验证
跑完嵌入后第一件事是算信噪比,而不是直接听。信噪比只表示整体能量差异,不能完全代表人耳主观感受,但能快速发现参数是否踩线。我一般同时输出全局 SNR 和分段 SNR,分段能暴露是不是某一帧被改得特别重。
noise = stego - original; snr = 10 * log10(sum(original.^2) / sum(noise.^2)); fprintf('全局SNR = %.2f dB\n', snr); frameNoise = reshape(noise(1:nFrames*frameLen), frameLen, nFrames); segEnergy = sum(frameNoise.^2, 1); medSNR = median(segEnergy); fprintf('分段SNR中位数 = %.2f dB\n', 10*log10(mean(original(1:nFrames*frameLen).^2)/medSNR));实测经验是:全局 SNR 在 20 dB 左右时,多数音乐素材听不出明显问题,语音素材会更敏感,因为静音段的量化噪声容易被放大。若分段 SNR 中位数低于 15 dB,先把delta下调 30% 再重测,不要直接改小波基,小波基对 SNR 的影响远小于量化步长。
4. 用 ANORD 思路从 DCT 系数中提取音频水印
4.1 提取核心:量化奇偶投票
提取时完全不依赖原始音频,只凭受攻击后的系数奇偶性恢复比特。这个特性让方案能用在盲提取场景,比如通过微信语音传输后再解码,或者从平台压缩过的音频里找回标识。
function bits_hat = extract_audio_dwt_dct(stego, opts) frameLen = opts.frameLen; nFrames = floor(length(stego)/frameLen); bits_hat = zeros(1, nFrames); for k = 1:nFrames frame = stego((k-1)*frameLen+1 : k*frameLen); % 与嵌入端相同的前向变换 ca = frame; for j = 1:opts.level [ca, ~] = dwt(ca, opts.wname); end C = dct(ca); % 量化到网格并取奇偶性 q = round(C(opts.idx) / opts.delta); frameBits = mod(q, 2); % ANORD: 用噪声估计筛选有效投票系数 high = C(40:min(120, length(C))); sigma_n = 1.4826 * median(abs(high)); valid = abs(C(opts.idx)) > 3 * sigma_n; if sum(valid) == 0 valid(:) = 1; % 全被筛掉时退回全部投票 end bits_hat(k) = mode(frameBits(valid)); end end提取得出mode(frameBits(valid))是在做多数投票。20 个系数里只要有 11 个保持正确奇偶,这一帧的比特就不会反。ANORD 在这里的作用是剔除那些幅度接近噪声底的系数,它们的奇偶往往在一两次滤波后就不稳定。
4.2 单稳态判据把抖动阈值变成稳定比特
提取端直接输出mode结果有个隐患:当帧内有效系数数恰好接近一半,或者音频处于静音段,比特会来回跳变。单稳态判据的思路是给比特判决加一个“滞回”区间,只有连续多帧保持同一状态时才切换输出。
% 单稳态去抖抽判决 persistent held_bit drift_count % 仅在单会话处理中使用 if isempty(held_bit), held_bit = 0; drift_count = 0; end raw = double(raw_bits(k)); if raw == held_bit drift_count = 0; else drift_count = drift_count + 1; if drift_count >= 3 held_bit = raw; drift_count = 0; end end bits_smooth(k) = held_bit;这个逻辑借鉴了单稳态触发器的不稳定状态保持特征:输入跳变不会立即改变输出,必须持续保持新电平一定周期才翻转。在音频水印里,它能有效对抗突发噪声和码率转换造成的孤立比特错误。
4.3 攻击测试:重采样、MP3 压缩与噪声叠加
验证鲁棒性时不用真跑去各平台转码,MATLAB 里模拟三步就够。第一步重采样到 22050 Hz 再回采到原始采样率,考察频谱拉伸对 DCT 系数位置的影响;第二步用audioread配合mp3write或模拟有损编码,没有额外工具箱时可以用低通滤波加抖动模拟;第三步直接叠高斯白噪声。
% 模拟攻击并逐帧提取 attack = stego; attack = resample(attack, 1, 2); % 降到 22050 Hz attack = resample(attack, 2, 1); % 恢复 44100 Hz,模拟重采样 attack = attack + 0.01 * randn(size(attack)); % 加噪声 resBits = extract_audio_dwt_dct(attack, opts); origBits = bits(1:length(resBits)); [~, ber] = biterr(origBits, resBits); fprintf('重采样+噪声后 BER = %.4f\n', ber);| 攻击类型 | 参数设置 | 实测 BER |
|---|---|---|
| 无攻击 | - | 0 |
| 重采样 | 44.1k→22.05k→44.1k | 0.008 |
| 高斯噪声 | SNR 约 30 dB | 0.005 |
| 低通滤波 | 截止 10 kHz | 0.012 |
| 重采样+噪声 | 两者叠加 | 0.031 |
BER 在 0.03 以下,配合同步帧头就能在真实场景使用。若 BER 超过 0.05,优先调大delta,其次检查idx是否包含太多幅值过小的系数。
5. 三个必调参数:量化步长 Δ、嵌入强度 α 与 DWT 分解层数
三个参数决定整个系统的容量、鲁棒性和听感。量化步长delta控制 DCT 系数量化网格间距;嵌入强度alpha在叠加式嵌入方法中控制伪随机序列幅度;level控制小波分解深度,间接决定嵌入子带的位置和系数数量。三者常常需要来回调两三轮才能达到平衡。
| 参数 | 推荐区间 | 调大后的效果 | 调大后的代价 |
|---|---|---|---|
| delta | 0.02~0.2 | BER 下降明显 | 量化噪声变大 |
| alpha | 0.02~0.3 | 检测相关峰更高 | 峰值削波、可听噪声 |
| level | 2~5 | 抗低通滤波更强 | 帧长需求高、重构误差累积 |
用叠加式嵌入时,alpha与delta之间有一个经验换算:量化步长产生的最大偏移是delta/2,而叠加伪随机序列的峰值幅度是alpha。想让两种方案的抗扰动能力接近,就取alpha ≈ delta/4。我在参数扫描时通常固定level=3,先扫delta从 0.02 到 0.2,记录每档的 BER 和 SNR,画出一条权衡曲线再选点。
参数扫描的落地方式是把embed_audio_dwt_dct和extract_audio_dwt_dct包在双层循环里。外层遍历delta,内层固定level,调用biterr记录误码率,同时计算分段 SNR 作为横轴。最优参数往往不是 BER 最低点,而是 BER 曲线出现平台期的起点,这表示再增大delta已无明显收益,反而徒增噪声。另一个实用技巧是先用同一帧反复测试单步还原误差:对某一帧先嵌入再提取,比较原始系数与量化系数的偏差,确保偏差不超过delta/2 + 0.05*sigma_n,这样整个系统的鲁棒性才有底。验证到这一步后,再把音频换成人声和钢琴曲各测一遍,确认参数没有偏向某类素材,就可以放心接入业务里的音频标识场景了。
本文还有配套的精品资源,点击获取