检测录音中的峰值数量

机器算法验证 r 信号处理
2022-02-03 14:28:05

我试图弄清楚如何检测录音语料库中的音节数量。我认为一个好的代理可能是波文件中的峰值。

这是我用英语说的文件尝试的(我的实际用例是斯瓦希里语)。此示例录音的文字记录是:“这是我尝试使用计时器功能。我正在查看暂停、发声。” 这段话共有22个音节。

wav 文件:https ://www.dropbox.com/s/koqyfeaqge8t9iw/test.wav?dl=0

R 中的seewave包很棒,并且有几个潜在的功能。首先,导入波形文件。

library(seewave)
library(tuneR)
w <- readWave("YOURPATHHERE/test.wav")  
w
# Wave Object
# Number of Samples:      278528
# Duration (seconds):     6.32
# Samplingrate (Hertz):   44100
# Channels (Mono/Stereo): Stereo
# PCM (integer format):   TRUE
# Bit (8/16/24/32/64):    16

我尝试的第一件事是timer()功能。它返回的一件事是每次发声的持续时间。此功能识别 7 个发声,远远少于 22 个音节。快速浏览一下情节表明发声不等于音节。

t <- timer(w, threshold=2, msmooth=c(400,90), dmin=0.1)
length(t$s)
# [1] 7

在此处输入图像描述

我还尝试了 fpeaks 函数而不设置阈值。它返回了 54 个峰值。

ms <- meanspec(w)
peaks <- fpeaks(ms)

在此处输入图像描述

这会根据频率而不是时间绘制幅度。添加一个等于 0.005 的阈值参数会滤除噪声并将计数减少到 23 个峰值,这与实际的音节数 (22) 非常接近。

在此处输入图像描述

我不确定这是最好的方法。结果会对阈值参数的值敏感,我必须处理大量文件。关于如何编码以检测代表音节的峰值的任何更好的想法?

4个回答

我不认为以下是最好的解决方案,但@eipi10 有一个很好的建议,可以在 CrossValidated 上查看这个答案所以我做了。

一种通用的方法是平滑数据,然后通过将局部最大值滤波器与平滑滤波器进行比较来找到峰值。

第一步是创建argmax函数:

argmax <- function(x, y, w=1, ...) {
  require(zoo)
  n <- length(y)
  y.smooth <- loess(y ~ x, ...)$fitted
  y.max <- rollapply(zoo(y.smooth), 2*w+1, max, align="center")
  delta <- y.max - y.smooth[-c(1:w, n+1-1:w)]
  i.max <- which(delta <= 0) + w
  list(x=x[i.max], i=i.max, y.hat=y.smooth)
}

它的返回值包括局部最大值 (x) 的参数——它回答了问题——以及这些局部最大值出现的 x 和 y 数组的索引 (i)。

我对绘图函数做了一些小的修改test:(a) 显式定义 x 和 y,(b) 显示峰值数量:

test <- function(x, y, w, span) {
  peaks <- argmax(x, y, w=w, span=span)

  plot(x, y, cex=0.75, col="Gray", main=paste("w = ", w, ", span = ", 
                                              span, ", peaks = ", 
                                              length(peaks$x), sep=""))
  lines(x, peaks$y.hat,  lwd=2) #$
  y.min <- min(y)
  sapply(peaks$i, function(i) lines(c(x[i],x[i]), c(y.min, peaks$y.hat[i]),
                                    col="Red", lty=2))
  points(x[peaks$i], peaks$y.hat[peaks$i], col="Red", pch=19, cex=1.25)
}

就像fpeaks我在最初的问题中提到的方法一样,这种方法也需要大量的调整。我不知道“正确”的答案(即音节/峰值的数量),所以我不确定如何定义决策规则。

par(mfrow=c(3,1))
test(ms[,1], ms[,2], 2, 0.01)
test(ms[,1], ms[,2], 2, 0.045)
test(ms[,1], ms[,2], 2, 0.05)

在此处输入图像描述

在这一点上fpeaks对我来说似乎不那么复杂,但仍然不令人满意。

我在分析蛋白质电泳图谱时遇到了类似的问题。我通过在配置文件的第二个衍生物上应用 msprocess R 包的一些功能来解决它​​们(参见https://fr.wikipedia.org/wiki/D%C3%A9pouillement_d 'une_courbe#Position_et_hauteur_du_pic)。这已在此处发布:http: //onlinelibrary.wiley.com/doi/10.1111/1755-0998.12389/abstract;jsessionid= 8EE0B64238728C0979FF71C576884771.f02t03

我不知道类似的解决方案是否适合您。祝你好运

我想建议一个使用该changepoint软件包的解决方案。下面的简单示例尝试通过查看可用数据中的一个通道来识别峰值,此处定义为变化点。

例子

数据来源

# Libs
library(seewave)
library(tuneR)

# Download
tmpWav <- tempfile(fileext = ".wav")
download.file(url = "https://www.dropbox.com/s/koqyfeaqge8t9iw/test.wav?dl=0",
              destfile = tmpWav)

# Read
w <- readWave(filename = tmpWav)

数据准备

# Libs
require(changepoint)

# Create time series data for one channel as an example
leftTS <- ts(data = w@left)

## Preview
plot.ts(leftTS)

plot.ts通过调用生成的图表: 频道作为时间序列

变点分析

changepoint软件包提供了许多用于识别数据变化/峰值的选​​项。下面的代码仅提供了一个使用BinSeg方法查找3 个峰值的简单示例:

# BinSeg method (example)
leftTSpelt <- cpt.var(data = leftTS, method = "BinSeg", penalty = "BIC", Q = 3)
## Preview
plot(leftTSpelt, cpt.width = 3)

得到的图表: 一些变化点 也可以得到值:

cpts(leftTSpelt)
[1]  89582 165572 181053

旁注

提供的示例主要关注说明如何将变化点分析应用于提供的数据;应谨慎对待传递给cp.var函数的参数。以下论文中给出了该软件包和可用功能的详细说明:

Killick, Rebecca 和 Eckley, Idris (2014)变更点:用于变更点分析的 R 包。统计软件杂志,58 (3)。第 1-19 页。

ecp

ecp,是另一个值得一提的R包。ecp有助于进行非参数多变量变化点分析,如果想要识别跨多个通道发生的变化点,这可能很有用。

是我之前在尝试通过在自相关函数中找到峰值来估计周期性时使用的 Python 库。

它使用一阶差分/离散导数进行峰值检测,并支持通过阈值和最小距离(连续峰值之间)参数进行调整。还可以使用高斯密度估计和插值来提高峰值分辨率(参见链接)。

它对我来说开箱即用,无需太多调整,即使对于嘈杂的数据也是如此。试试看。