如何区分声音和打鼾?

信息处理 fft 噪音 频谱 语音
2022-01-18 00:07:03

背景: 我正在开发一个 iPhone 应用程序( 其他几篇 文章中提到),它在一个人睡着时“听”打鼾/呼吸,并确定是否有睡眠呼吸暂停的迹象(作为“睡眠实验室”的预屏幕)测试)。该应用程序主要使用“光谱差异”来检测打鼾/呼吸,并且在针对睡眠实验室记录(实际上非​​常嘈杂)进行测试时,它工作得很好(相关性约为 0.85--0.90)。

问题: 大多数“卧室”噪音(风扇等)我可以通过多种技术过滤掉,并且通常可以可靠地检测人耳无法检测到的 S/N 水平的呼吸。问题是语音噪音。在后台播放电视或收音机(或只是让远处有人说话)并不罕见,并且声音的节奏与呼吸/打鼾密切相关。事实上,我通过应用程序运行了已故作家/讲故事者比尔霍尔姆的录音,它与打鼾的节奏、水平变化和其他几个指标基本上没有区别。(尽管我可以说他显然没有睡眠呼吸暂停,至少在清醒时没有。)

所以这有点长镜头(可能还有一些论坛规则),但我正在寻找一些关于如何区分声音的想法。我们不需要以某种方式过滤掉鼾声(认为那会很好),而是我们只需要一种方法来拒绝被语音过度污染的“太吵”的声音。

有任何想法吗?

发布的文件:我在 dropbox.com 上放置了一些文件:

第一个是一段相当随意的摇滚(我猜)音乐,第二个是已故比尔霍尔姆讲话的录音。两者(我用它作为我的“噪声”样本与打鼾区分开来)都与噪声混合以混淆信号。(这使得识别它们的任务变得更加困难。)第三个文件是你真正记录的十分钟,其中前三分之一主要是呼吸,中间三分之一是混合呼吸/打鼾,最后三分之一是相当稳定的打鼾。(你会咳嗽以获得奖金。)

所有三个文件都已从“.wav”重命名为“_wav.dat”,因为许多浏览器使得下载 wav 文件变得异常困难。下载后只需将它们重命名为“.wav”。

更新:我认为熵对我来说是“诀窍”,但事实证明它主要是我使用的测试用例的特殊性,加上一个设计得不太好的算法。在一般情况下,熵对我的作用很小。

随后,我尝试了一种技术,在该技术中,我计算了整体信号幅度(我尝试了功率、频谱通量和其他几个测量值)的 FFT(使用几个不同的窗口函数),每秒采样大约 8 次(从主 FFT 周期中获取统计数据)即每 1024/8000 秒)。对于 1024 个样本,这涵盖了大约两分钟的时间范围。由于打鼾/呼吸与声音/音乐的节奏缓慢(并且这也可能是解决“可变性”问题的更好方法),我希望能够看到其中的模式,但是虽然有提示这里和那里的模式,没有什么我可以真正抓住的。

更多信息:在某些情况下,信号幅度的 FFT 会产生一个非常明显的模式,在大约 0.2Hz 和阶梯谐波处有一个强峰值。但这种模式在大多数情况下并不是那么明显,并且语音和音乐可以产生不太明显的类似模式的版本。可能有某种方法可以计算品质因数的相关值,但似乎需要曲线拟合到大约 4 阶多项式,并且在手机中每秒执行一次似乎不切实际。)

我还尝试对我将频谱分成的 5 个单独的“波段”进行平均幅度的相同 FFT。频带是 4000-2000、2000-1000、1000-500 和 500-0。前 4 个频段的模式通常与整体模式相似(尽管没有真正的“突出”频段,并且在较高频段中通常会出现非常小的信号),但 500-0 频段通常只是随机的。

赏金: 我将赏金给内森,即使他没有提供任何新的东西,因为他是迄今为止最有成效的建议。不过,如果他们提出了一些好主意,我仍然愿意将一些积分奖励给其他人。

4个回答

背景

根据以下论文,打鼾的特点是在 130Hz 左右达到峰值,并且完全集中在 12kHz 以下:

让我们看看我们是否可以利用这一点。

MATLAB 示例

我们对孩子打鼾的记录很差一个 10 分钟的 8 位单声道 WAV 文件。采样率为 8KHz,即音频信号的带宽为 4KHz。水平非常低,所以我会先压缩它。

[snd,fs]=wavread('recordedFile20120408010300_first_ten_minutes');
cmp=compand(snd,255,1);
wavwrite(cmp,'companded'); % used for listening purposes
[s,f,t,p]=spectrogram(snd,hann(8192));
surf(linspace(0,600,length(t)),f/pi,10*log10(p),'edgecolor','none'); 
axis tight; view(0,90);

全谱图

y 轴归一化为带宽 4KHz,因此您在 0.1 处看到的凹口对应于 400Hz 的频率。在约 186 秒时出现与咳嗽相对应的尖峰;忽略它。我们可以隐约看到每次打鼾时的凹痕。不仅如此,它们似乎集中在 0.2 x 4KHz=800Hz 以下。让我们仔细看看。

idx_max_freq=round(0.2*length(f));
surf(linspace(0,600,length(t)),fs*f(1:,idx_max_freq:)/(2*pi),10*log10(p(1:idx_max_freq,:)),'edgecolor','none');
axis tight; view(0,90);

将频谱图缩放到 0-800Hz。

这次频率轴以赫兹为单位。现在这些缺口已经很清楚了。我们甚至可以看到从 60Hz(180Hz、300Hz、420Hz)开始的电源线噪声的泛音。现在是算法的本质:让我们根据这个子带中的能量对信号进行分类,去除线路噪声。

freq_list=round([1:57 63:177 183:297 303:417 423:800]*idx_max_freq/800);
y=10*log10(sum(p(freq_list,:)));
plot(linspace(0,600,length(y)),y-median(y))
stem(linspace(0,600,length(y)),y-median(y)>.5*std(y))

如果我们想变得花哨,我们可以丢弃超大的尖峰:

stem(linspace(0,600,length(y)),(y-median(y)>.5*std(y)).*(y-median(y)<5*std(y)))

陷波子带 0-800Hz 中的能量图

最终结果

低信噪比表现在第一个图中难以识别信号,这意味着我们只有半个标准差(其值为 4.1)的余地。茎标志着打鼾。

只是把它扔在这里以涵盖所有可能性,您可能可以使用熵,我不知道打鼾与语音的熵水平是多少,但如果它足够不同,可能会起作用。 http://www.ee.columbia.edu/~dpwe/papers/ShenHL98-endpoint.pdf

也许是时域统计?打鼾似乎具有相对较长的稳定状态,而语音能量在较短的时间内变化很大。这也可以与光谱分析相结合。元音有更多的低频内容,辅音有更多的高频。在语音期间,频谱可能会在这些状态之间快速来回反弹,而存储可能会在一种状态下停留更长的时间。

随时间变化的光谱复杂度。我假设人类语音可能使用更多的音素,并且它们的排序比打鼾的音素序列具有更大的统计复杂性。

这可能是一个比连续语音识别更容易的问题,因为您不需要真正正确识别任何特定的音素或句子,只需要音素发声频谱段的数量,以及它们序列的一些统计复杂性度量(熵或可压缩性测试可能有效)。然后看看您是否可以确定这些措施的可靠阈值。