如何在半复杂场景中正确检测局部最大值和曲线窗口?

我有一系列数据,需要在一定数量的读数(窗口大小)内检测系列中的峰值,并排除一定程度的背景“噪音”。 我还需要捕捉可感知曲线的起点和终点(即,当它开始滴答时,然后当它停止滴答时)。

数据是高精度浮子。

这是一个快速草图,它捕捉了我在视觉上遇到的最常见情况: 在此处输入图像描述

我尝试的一种方法是沿着曲线向后通过一个大小为X的窗口以检测峰值。 它开始运作良好,但我错过了最初没有预料到的很多条件。 我开始研究的另一种方法是增长窗口,可以发现更长的持续时间曲线。 另一种方法使用基于更多微积分的方法来监视一些速度/梯度方面。 似乎没有人达到最佳位置,可能是由于我缺乏统计分析经验。

也许我需要使用某种统计分析软件包来覆盖我的基础而不是编写我自己的算法? 或者是否有一种有效的方法可以使用某种本地最大技术直接使用SQL来解决这个问题? 我只是不确定如何有效地处理这个问题。 我尝试的每种方法似乎都会丢失各种阈值,检测太多峰值或不捕获整个事件(在读取过程中过早报告峰值数据点)。

最终这是在Ruby中实现的,所以如果你可以建议用Ruby来解决这个问题的最有效和最正确的方法,那么也会接受一种语言无关的算法方法 。 或者是否有某个库可以解决我在检测最大峰值的情况下遇到的各种问题?

我的想法很简单,在获得您感兴趣的窗口之后,您需要找到此窗口中的所有峰值,您可以将最后一个值与下一个值进行比较,之后您将获得峰值出现的位置,您可以确定最佳位置峰。

我在matlab中写了一个简单的来源来展示我的想法!

我的例子是来自音频文件的wave 🙂

waveFile='Chick_eco.wav'; [y, fs, nbits]=wavread(waveFile); subplot(2,2,1); plot(y); legend('Original signal'); startIndex=15000; WindowSize=100; endIndex=startIndex+WindowSize-1; frame = y(startIndex:endIndex); nframe=length(frame) %find the peaks peaks = zeros(nframe,1); k=3; while(k <= nframe - 1) y1 = frame(k - 1); y2 = frame(k); y3 = frame(k + 1); if (y2 > 0) if (y2 > y1 && y2 >= y3) peaks(k)=frame(k); end end k=k+1; end peaks2=peaks; peaks2(peaks2<=0)=nan; subplot(2,2,2); plot(frame); legend('Get Window Length = 100'); subplot(2,2,3); plot(peaks); legend('Where are the PEAKS'); subplot(2,2,4); plot(frame); legend('Peaks in the Window'); hold on; plot(peaks2, '*'); for j = 1 : nframe if (peaks(j) > 0) fprintf('Local=%i\n', j); fprintf('Value=%i\n', peaks(j)); end end %Where the Local Maxima occur [maxivalue, maxi]=max(peaks) 

替代文字

你可以看到所有的山峰及其发生的地方

本地= 37

值= 3.266296e-001

本地= 51

值= 4.333496e-002

本地= 65

值= 5.049438e-001

本地= 80

值= 4.286804e-001

本地= 84

值= 3.110046e-001

我会提出几个不同的想法。 一种是使用离散小波,另一种是使用地理学家的突出概念。

小波:对数据应用某种小波分解。 有多种选择,Daubechies小波是最广泛使用的。 你想要低频峰值。 将高频小波元素清零,重建数据并寻找局部极值。

突出:那些嘈杂的山峰和山谷是地理学家关注的重点。 他们想要准确地知道山的多个小山峰中哪一个是最高的,山谷中最低点的确切位置。 在数据集中查找本地最小值和最大值。 你应该有一个min / max / min / max /…/min的序列。 (您可能希望添加低于全局最小值的任意端点。)考虑最小/最大/最小序列。 根据两个最小值的最大值和较大值之间的差异对这些三元组中的每一个进行分类。 制作一个简化的序列,用两个最小值中较小的一个替换这些三元组中最小的三元组。 迭代直到达到单个最小/最大/最小三倍。 在您的示例中,您需要下一层,即最小/最大/最小/最大/最小序列。

注意:我将描述算法步骤,就好像每次传递都是不同的。 显然,在特定的实现中,您可以组合对您的应用程序有意义的步骤。 为了我的解释,它使文本更清晰。

我将对你的问题做一些假设:

  1. 感兴趣的窗口(您正在寻找的信号)覆盖整个数据空间的一小部分(即,它不是一个长信号)。
  2. 窗口具有显着的范围(即,它们在图片上不是一个像素宽)。
  3. 窗口具有最小的感兴趣峰值(即,即使信号超过背景噪声,峰值也必须具有超出背景的额外信号)。
  4. 窗口永远不会重叠(即,每个窗口都可以作为与信号其余部分无关的独特子问题进行检查)。

鉴于这些,您可以首先查看您的数据流中的一组感兴趣的窗口。 您可以通过首先通过数据来完成此操作:从左向右移动,查找噪声阈值交叉点。 如果信号低于噪声基底并且在下一个样本上超过它,那么这是窗口的候选起点(反之亦然,候选终点)。

现在通过候选窗口:将每个窗口的范围和内容与上面定义的值进行比较。 以图片为例,图像左侧的小峰几乎不会超过本底噪声,并且时间太短。 然而,屏幕中心的窗口明显具有宽的时间范围和显着的最大值。 保持符合最低标准的窗户,丢弃那些微不足道的窗户。

现在详细检查剩余的窗口(请记住,它们可以单独处理)。 峰值很容易找到:通过窗口并保持局部最大值。 关于信号的前沿和后沿,您可以看到图像中有一个窗口,该窗口略大于信号超过本底噪声的实际点。 在这种情况下,您可以使用有限差分近似来计算信号的一阶导数。 您知道前缘将位于图表窗口左侧的某个位置:查找一阶导数超过其自身的正噪声基底的点(斜率急剧上升)。 对后缘执行相同操作(始终位于窗口右侧)。

结果:一组时间窗口,信号的前沿和后沿以及该窗口中出现的峰值。

看起来窗口的定义是y的范围,y高于阈值。 所以用它来确定窗口的大小。 在其中,找到最大值,从而找到峰值。

如果失败了,那么您有什么额外的标准来定义感兴趣的区域? 您可能需要将隐含的假设确定为“对我而言似乎是一个高峰”。