Winform 动态画曲线 波峰波谷识别

Posted HelloLLLLL

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Winform 动态画曲线 波峰波谷识别相关的知识,希望对你有一定的参考价值。

项目需要识别数组的波峰波谷,我就想 可视化的测试自己的判断波峰波谷的算法,于是就有了下面这张图。
我就用gdi+再panel上描点,点画完后,就点击分析按钮蓝色的为波峰 绿色的为波谷。虽然说能识别出来,但是对于波峰来说识别的点对于项目来说有些生硬。比如这两点
按照项目来讲 384也应算作峰顶,但是我目前还没实现。我的实现的思路是这样,定义一个diff值,比如10,再387这里是最大值,然后往前循环,如果前一个值和387相差<10,再往前找,并判断前面的点与387这个点的时间间隔是否>1秒 如果相差>10或者间隔>1秒就停止循环。
 
算法的思路是这样,首先判断曲线走势,为上升的话, 找到第一个封顶,为下降的话,第一个点就是峰顶。然后找峰谷 ,一个循环就结束了。
代码:
private void Recognize()
        {

            Font font = new Font("宋体", 10);

            if (LstDp.Count < 2)
            {
                return;
            }

            bIsScanStart = true;

            DataPoint dpDown = null;

            for (int i = 1; i < LstDp.Count; i++)
            {

                if (bIsScanStart)
                {

                    iSIndex = i - 1;
                    //判断开始是否上升
                    bStartIsUp = ChargeIsStartUp(iSIndex, LstDp);

                    dpDown = LstDp[iSIndex];

                    bIsScanStart = false;
                }

                var dS = LstDp[i - 1];

                var dNext = LstDp[i];

                int topIndex = 0;

                if (bStartIsUp)//开始是上升的
                {

                    while (i + 1 < LstDp.Count)
                    {

                        dS = LstDp[i];

                        dNext = LstDp[i + 1];

                        ++i;

                        if (dNext.Val - dS.Val < 0)//到达峰顶dS
                        {

                            LstPtTop.Add(dS);

                            topIndex = LstPtTop.Count - 1;
                            break;

                        }

                    }

                }

                else
                {
                    LstPtTop.Add(dpDown);
                    topIndex = LstPtTop.Count - 1;
                }

                DataPoint temp = LstPtTop[topIndex];

                int j = i + 1;

                while (j < LstDp.Count)
                {

                    var dTopNext = LstDp[j];

                    var dTopS = LstDp[j - 1];

                    j++;

                    if (dTopNext.Val - dTopS.Val > 0)//峰谷dTops
                    {

                        LstDwn.Add(dTopS);
                        bIsScanStart = true;
                        break;
                    }

                    else
                    {

                        continue;

                    }
                }

                i = j - 1;

                if (i == LstDp.Count() - 1)
                {
                    if (LstDp[i].Val - LstDp[i - 1].Val > 0)
                    {
                        LstPtTop.Add(LstDp[i]);
                    }
                }
            }
            for (int i = 0; i < LstPtTop.Count; i++)
            {

                g.DrawString("峰顶", font, Brushes.Blue, new Point(LstPtTop[i].pt.X,LstPtTop[i].pt.Y-10));

            }

            for (int i = 0; i < LstDwn.Count; i++)
            {

                g.DrawString("峰谷", font, Brushes.DarkGreen, new Point(LstDwn[i].pt.X, LstDwn[i].pt.Y + 5));

            }
        }

下载:

代码写的很随意,主要是思路。

图像分析:投影曲线的波峰查找

原文:图像分析:投影曲线的波峰查找

1. 前言

在图像分析里,投影曲线是我们经常要用到的一个图像特征,通过投影曲线我们可以看到在某一个方向上,图像灰度变化的规律,这在图像分割,文字提取方面应用比较广。一个投影曲线,它的关键信息就在于波峰与波谷,所以我们面临的第一个问题就是找到波峰与波谷。

第一次涉及到求波峰与波谷时,很多人都不以为意,觉得波谷波峰还不容易,无非是一些曲线变化为零的点,从离散的角度来说,也就是:

波峰:$F(x)>F(x-1) 且 F(x)>F(x+1)$

波谷:$F(x)<F(x-1) 且 F(x)<F(x+1)$

这么简单吗?显示不是,你首先就会遇到这样的曲线图,然后图上的波峰点并不满足上面的条件。

技术分享图片

看到这种情况,你也许会考虑在上面的等式中把$>$和$<$改为$ge$和$le$。

波峰:$F(x)ge F(x-1) &&  F(x) > F(x+1)$  或者 $F(x)> F(x-1) &&  F(x) ge F(x+1)$

波谷:$F(x)le F(x-1) &&  F(x) < F(x+1)$  或者 $F(x)< F(x-1) &&  F(x) le F(x+1)$

这次是否就这样简单,答案显示不是,下面的这个图就会让你对一些非峰值点作出错误的判断。

技术分享图片

上面这幅图真正的峰值只有一个,其他平台上的点,你如果按上面修改的公式,就会被错误的当成波峰点。

下面让我们看一下,到底如何能求得准确的曲线波峰与波谷。

2. 波峰波谷算法

投影曲线实际上是一个一维的向量:

$$V=[v_1,v_2,dots,v_n]$$

其中$v_i,i in [1,2,dots,N]$,代表图像在第$i$行或列上的灰度累积。当然不仅仅是投影曲线,$T$也可以是某一事件中变量的观测值,我们需要研究这个变量的变化规律。

下面给出波峰与波谷的算法:

1,假投影曲线可以表示为$V=[v_1,v_2,dots,v_n]$。

2,计算V的一阶差分向量$Diff_V$:

$$Diff_v(i)=V(i+1)-V(i),其中iin {1,2,dots,N-1}$$

3,对差分向量进行取符号函数运算,$Trend=sign(Diff_v)$,即遍历$Diff_v$,若$Diff_v(i)$大于0,则取1;如果小于0,则取-1,否则则值为0。

$$ sign(x)=left{
egin{aligned}
1& if x>0 \\ 
0& if x=0 \\ 
-1& if x<0  
end{aligned}
ight.
$$

4,从尾部遍历$Trend$向量,进行如下操作:

$$
if Trend(i)=0且Trend(i+1)ge0,则Trend(i)=1 \\
if Trend(i)=0且Trend(i+1)<0,则Trend(i)=-1
$$

5,对$Trend$向量进行一阶差分运算,如同步骤2,得到$R=diff(Trend)$。

6,遍历得到的差分向量$R$,如果$R(i)=-2$,则$i+1$为投影向量$V$的一个峰值位,对应的峰值为$V(i+1)$;如果$R(i)=2$,则$i+1$为投影向量$V$的一个波谷位,对应的波谷为$V(i+1)$。

下面我们来结合一个实际的向量值,给中中间结合的计算。

1,$V=[-5,10,10,14,14,8,8,6,6,-3,2,2,2,2,-3]$。

它的曲线图像如下把示,图中红色圈标出了曲线的峰值,而绿字圈标出了图像的波谷位置。

技术分享图片

2,计算$V$的一阶差分,我们得到$Diff(V)=[15,0,4,0,,-6,0,-2,0,-9,5,0,0,0,-5]$。

3,对$Diff_v$进行取符号运算,得到向量$Trend=[1,0,1,0,-1,0,-1,0,-1,1,0,0,0,-1]$。

4,对$Trend$作一次遍历,如步骤4。$Trend=[1,1,1,-1,-1,-1,-1,-1,-1,1,-1,-1,-1,-1]$。

5,对$Trend$做一阶差分,得到向量$R=Diff(Trend)=[0,0,-2,,0,0,0,0,0,2,-2,0,0,0]$。

6,遍历向量$R$,我们就得到了两个峰值点和一个波谷点。

3. 算法原理

其实上述算法的核心思路非常简单,曲线的峰值点,满足一阶导数为0,并且满足二阶导数为负;而波谷点,则满足一阶导数为0,二阶导数为正。

在上面的算法里面,我们首先计算了一阶的导数$Diff_v$,然后我们将其符号化,是因为我们并不关心一阶导数的大小。

然后我们去看那些一阶层数为0的地方,我们发现,那些平台上的点,有些并不是波峰与波谷,然后很多处在上坡与下坡的路上,所以我们将它们的一阶导数设为与它们所在的坡面梯度方向相同。

最后我们再来计算二阶导数时,就会发现只要为2或者-2,所以曲线斜在这个点发生了变化,由正变负或由负变正。找到这些点,也就找到了原曲线中的波峰或波谷点。

4. 实现

下面给出这个算法的C++实现,findPeaks是查找波峰函数,而查找波谷函数则类似,这里没有写在一个函数内。函数接受一个Vecotr<int>的向量,输出为一个vector<int>的位置向量。

void findPeak(const vector<int>& v, vector<int>& peakPositions)
{
    vector<int> diff_v(v.size() - 1, 0);
    // 计算V的一阶差分和符号函数trend
    for (vector<int>::size_type i = 0; i != diff_v.size(); i++)
    {
        if (v[i + 1] - v[i]>0)
            diff_v[i] = 1;
        else if (v[i + 1] - v[i] < 0)
            diff_v[i] = -1;
        else
            diff_v[i] = 0;
    }
    // 对Trend作了一个遍历
    for (int i = diff_v.size() - 1; i >= 0; i--)
    {
        if (diff_v[i] == 0 && i == diff_v.size() - 1)
        {
            diff_v[i] = 1;
        }
        else if (diff_v[i] == 0)
        {
            if (diff_v[i + 1] >= 0)
                diff_v[i] = 1;
            else
                diff_v[i] = -1;
        }
    }

    for (vector<int>::size_type i = 0; i != diff_v.size() - 1; i++)
    {
        if (diff_v[i + 1] - diff_v[i] == -2)
            peakPositions.push_back(i + 1);
    }
}











以上是关于Winform 动态画曲线 波峰波谷识别的主要内容,如果未能解决你的问题,请参考以下文章

visual c++已经得到一个信号的波形,怎样检测波峰和波谷?

关于波峰波谷趋势分割(想象中的方法),判断趋势,突然来想到的,记下来,没有实验。以便以后用于分割

C# 思路分享 构造可进行单元测试的波形数据

hdu 4105 贪心思想

codeforces 14E. Camels(多维dp)

Python使用matplotlib可视化时间序列将时间序列数据的波峰和波谷进行使用自定义颜色的形状标记(Time Series with Peaks and Troughs Annotated)