Mel Spectrogram + Canny + Gaussian Demo
这是一个离线演示程序,生成以下内容:
original_mel.png: log10 Mel 频谱图 (Slaney mel + norm=’slaney’, 灰度图)
elements_only.png: Canny 边缘 (浊元音 + 塞音代理) + 高斯摩擦音层 (灰度图)
elements_thinning.png: 基于细化的边缘 + 高斯摩擦音层 (灰度图)
otsu_mask.png: 归一化 log-Mel 上的 Otsu 3 值阈值掩码 (灰度图)
laplacian_r3.png: log-Mel 上的拉普拉斯算子 (半径 3) (灰度图)
mfcc.png: MFCC 频谱图 (灰度图)
mfcc_thresh.png: 阈值化 MFCC (灰度图)
mel_from_mfcc_thresh.png: 从阈值化 MFCC 重建的 log10 Mel (灰度图)
peak_mask.png: 用于凸脊提取的垂直峰值掩码 (灰度图)
convex_ridge.png: 凸脊提取 (稀疏 + 水平 TV + 峰值掩码)
convex_ridge_hard.png: 硬峰值约束的凸脊提取对比图
convex_ridge_hard_lr.png: 硬峰值 + 低秩背景建模后的凸脊
convex_lowrank.png: 低秩背景分量 (灰度图)
convex_ridge_thin.png: 凸脊提取的峰值细化结果 (灰度图)
demo.html: 带参数的并排可视化演示
<input_stem>.wav: 处理后的音频,保存到 out/ 供页面内播放
<input_stem>_*.png: 每个音频的前缀图像副本 (用于多音频页面)
注意:边缘/高斯层是结构和类摩擦能量的 视觉代理,并非音素分类器。
安装
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
运行
python demo.py --input test.wav --outdir out --title "test.wav"
在浏览器中打开 out/demo.html。
如果存在多组 <stem>_*.png 图像集,页面会将每个 <stem>.wav 与其匹配的前缀图像分组显示。
固定参数
- SR=16000, n_fft=1024, win_length=1024, hop_length=256, window=Hann
- Mel: 80 bands, fmin=80, fmax=7600, Slaney scale + norm=’slaney’
- log10 amplitude with floor=1e-10
- Canny 输入: 鲁棒缩放 (5th/95th 百分位), 范围 [0,1]
- Canny (自定义): 垂直梯度强度, 垂直 NMS, 时间方向一致性
- Thinning (细化): 垂直梯度强度 + 水平膨胀 + Zhang-Suen 细化
- Canny 默认值: sigma=1.0, low=0.04, high=0.12, horiz_ratio=0.9, coh=7
- Thinning 默认值: sigma=0.8, threshold=0.08, horiz_ratio=0.9, coh=5, horiz_link=3
- Edge bias 常数: aniso=1.6, ratio_gamma=2.0, coh_power=1.5
- Fricative (摩擦音) 默认值: hi_freq=3000 Hz, ratio>0.55, energy>20th percentile
- Gaussian blur: sigma=(1.2 mel bins, 3.0 time frames)
- Otsu: 归一化 log-Mel 上的 3 类阈值, bins=256
- Laplacian: 高斯半径=3
- MFCC: n_mfcc=20 (来自 mel power)
- MFCC 阈值: percentile=75 (绝对值阈值)
- Convex ridge (凸脊) 默认值: l1=0.08, tv=0.12, peak=0.25, rho=1.2, iters=80
- Peak mask 默认值: percentile=70, smooth=1.0, dilate=1
- Convex ridge thinning: threshold=0.3, smooth=0.8, hi_start=80 Hz, hi_atten=0.3, coh_size=3, coh_frac=0.5
- Convex ridge low-rank: lambda=0.05, iters=15, inner=30
凸脊提取(稀疏 + 水平 TV + 峰值掩码)
设归一化 log-Mel 图像为 (Y \in \mathbb{R}+^{F \times T}),我们要提取的
脊线为 (X \in \mathbb{R}+^{F \times T})。先从 (Y) 沿频率轴做局部峰值检测
得到垂直峰值掩码 (M \in {0,1}^{F \times T})(可带分位数阈值与膨胀)。
定义时间差分算子(仅沿水平方向):
[
(D_t X){f,t} = X{f,t+1} - X_{f,t}, \quad t=1,\dots,T-1
]
软峰值约束(默认)
[
\min_{X \ge 0}\;
\frac{1}{2}|X - Y|_F^2
- \lambda_1 |X|_1
- \lambda_t |D_t X|_1
- \lambda_p |(1-M)\odot X|_1
\quad \text{s.t. } X \le Y
]
含义:
- (|X|_1):稀疏,让脊线更细。
- (|D_t X|_1):水平 TV,鼓励时间连续。
- (|(1-M)\odot X|_1):峰值掩码外的额外惩罚,把能量“推回”峰上。
- (X \le Y):不引入原谱中不存在的能量。
硬峰值约束(可选)
[
\min_{X \ge 0}\;
\frac{1}{2}|X - Y|_F^2
- \lambda_1 |X|_1
- \lambda_t |D_t X|_1
\quad \text{s.t. } X \le Y,\;\; X \odot (1-M) = 0
]
硬约束会强制脊线只能出现在峰值候选位置上。
说明
- 求解使用 ADMM:数据项 + (\ell_1)/TV 近端 + 盒约束投影。
- 峰值掩码取自 (Y)(而非 (X)),保证整体问题保持凸。