波峰波谷算法
投影曲线实际上是一个一维的向量:
![](https://i-blog.csdnimg.cn/blog_migrate/07b927a976bca6ce6d773082e5c0dd72.png)
![](https://i-blog.csdnimg.cn/blog_migrate/7f99f5b2410652854b6ae51a0303268e.png)
![](https://i-blog.csdnimg.cn/blog_migrate/c4b0bb760a04e8c0ba71ca8e8a9327e1.png)
![](https://i-blog.csdnimg.cn/blog_migrate/317b8f6cefd24533b49c8f97ae6c7e9d.png)
![](https://i-blog.csdnimg.cn/blog_migrate/cd6fba62df7747c2694aa12f3af35b4a.png)
![](https://i-blog.csdnimg.cn/blog_migrate/f09f971bd163a2a7f012faceeb8a9dfa.png)
![](https://i-blog.csdnimg.cn/blog_migrate/317b8f6cefd24533b49c8f97ae6c7e9d.png)
![](https://i-blog.csdnimg.cn/blog_migrate/4edbb1ec00140f3c006d5c3aa07f6224.png)
![](https://i-blog.csdnimg.cn/blog_migrate/f09f971bd163a2a7f012faceeb8a9dfa.png)
![](https://i-blog.csdnimg.cn/blog_migrate/5db2a4ad9ec1aa1a91a1d9a7ac0eb275.png)
![](https://i-blog.csdnimg.cn/blog_migrate/f09f971bd163a2a7f012faceeb8a9dfa.png)
![](https://i-blog.csdnimg.cn/blog_migrate/317b8f6cefd24533b49c8f97ae6c7e9d.png)
![](https://i-blog.csdnimg.cn/blog_migrate/5c1f4334fbae10d5ce88bbf3ef1bbdd9.png)
![](https://i-blog.csdnimg.cn/blog_migrate/392ad6b50bc9dce7476ccc8030517b02.png)
其中
,代表图像在第
行或列上的灰度累积。当然不仅仅是投影曲线,
也可以是某一事件中变量的观测值,我们需要研究这个变量的变化规律。
下面给出波峰与波谷的算法:
1,假投影曲线可以表示为
。
2,计算V的一阶差分向量
:
![](https://i-blog.csdnimg.cn/blog_migrate/7b55e230be944a3ede6da885bd39f617.png)
![](https://i-blog.csdnimg.cn/blog_migrate/bdb1cf17b24c43bffda2556745129fc3.png)
![](https://i-blog.csdnimg.cn/blog_migrate/6a1aa10093e08590dd997e63dd762744.png)
![](https://i-blog.csdnimg.cn/blog_migrate/6a1aa10093e08590dd997e63dd762744.png)
![](https://i-blog.csdnimg.cn/blog_migrate/e68bb5412edc0306818209f5d64f6385.png)
![](https://i-blog.csdnimg.cn/blog_migrate/c3522b22b8bc19c759bb63fdc8602261.png)
![](https://i-blog.csdnimg.cn/blog_migrate/bdb1cf17b24c43bffda2556745129fc3.png)
![](https://i-blog.csdnimg.cn/blog_migrate/f74c1d7fed0254f6a39d4b51871548a3.png)
![](https://i-blog.csdnimg.cn/blog_migrate/7f99f5b2410652854b6ae51a0303268e.png)
![](https://i-blog.csdnimg.cn/blog_migrate/07b927a976bca6ce6d773082e5c0dd72.png)
![](https://i-blog.csdnimg.cn/blog_migrate/c3522b22b8bc19c759bb63fdc8602261.png)
![](https://i-blog.csdnimg.cn/blog_migrate/bdb1cf17b24c43bffda2556745129fc3.png)
![](https://i-blog.csdnimg.cn/blog_migrate/d5f7d08ddf630aea282f0a0ed9d0595f.png)
![](https://i-blog.csdnimg.cn/blog_migrate/01284c94da51a82ec4cda6d676f664e8.png)
![](https://i-blog.csdnimg.cn/blog_migrate/f74c1d7fed0254f6a39d4b51871548a3.png)
![](https://i-blog.csdnimg.cn/blog_migrate/99e000b8ab2865de6d2a6cd6842afd74.png)
![](https://i-blog.csdnimg.cn/blog_migrate/07b927a976bca6ce6d773082e5c0dd72.png)
![](https://i-blog.csdnimg.cn/blog_migrate/c3522b22b8bc19c759bb63fdc8602261.png)
![](https://i-blog.csdnimg.cn/blog_migrate/bdb1cf17b24c43bffda2556745129fc3.png)
![](https://i-blog.csdnimg.cn/blog_migrate/f74c1d7fed0254f6a39d4b51871548a3.png)
![](https://i-blog.csdnimg.cn/blog_migrate/f09f971bd163a2a7f012faceeb8a9dfa.png)
![](https://i-blog.csdnimg.cn/blog_migrate/bdb1cf17b24c43bffda2556745129fc3.png)
![](https://i-blog.csdnimg.cn/blog_migrate/d0dfa7bff4fa5a140ab2a0e23f704382.png)
![](https://i-blog.csdnimg.cn/blog_migrate/01284c94da51a82ec4cda6d676f664e8.png)
![](https://i-blog.csdnimg.cn/blog_migrate/f09f971bd163a2a7f012faceeb8a9dfa.png)
![](https://i-blog.csdnimg.cn/blog_migrate/e5916ce3132b34e60f006bb264bf9485.png)
![](https://i-blog.csdnimg.cn/blog_migrate/f09f971bd163a2a7f012faceeb8a9dfa.png)
![](https://i-blog.csdnimg.cn/blog_migrate/5db2a4ad9ec1aa1a91a1d9a7ac0eb275.png)
![](https://i-blog.csdnimg.cn/blog_migrate/f09f971bd163a2a7f012faceeb8a9dfa.png)
![](https://i-blog.csdnimg.cn/blog_migrate/1ba1541370d6cd53efb43b85e87759ce.png)
![](https://i-blog.csdnimg.cn/blog_migrate/99e000b8ab2865de6d2a6cd6842afd74.png)
![](https://i-blog.csdnimg.cn/blog_migrate/01284c94da51a82ec4cda6d676f664e8.png)
3,对差分向量进行取符号函数运算,
,即遍历
,若
大于0,则取1;如果小于0,则取-1,否则则值为0。
![](https://i-blog.csdnimg.cn/blog_migrate/9e020a8d46b94b515294f329ad5a0596.png)
![](https://i-blog.csdnimg.cn/blog_migrate/bdb1cf17b24c43bffda2556745129fc3.png)
![](https://i-blog.csdnimg.cn/blog_migrate/729ab4572f4a9f91655ab39f8457d50d.png)
![](https://i-blog.csdnimg.cn/blog_migrate/cb82789ac4a4fd9e37f77c9025d6783e.png)
![](https://i-blog.csdnimg.cn/blog_migrate/c3522b22b8bc19c759bb63fdc8602261.png)
![](https://i-blog.csdnimg.cn/blog_migrate/3a1cc904c68ea707987f0b10e241a13e.png)
![](https://i-blog.csdnimg.cn/blog_migrate/f74c1d7fed0254f6a39d4b51871548a3.png)
![](https://i-blog.csdnimg.cn/blog_migrate/7f99f5b2410652854b6ae51a0303268e.png)
![](https://i-blog.csdnimg.cn/blog_migrate/fff43ad43bea230f57ff3c71b8c9647d.png)
![](https://i-blog.csdnimg.cn/blog_migrate/a2f4ffc41fac282e75a6aca5b91e069e.png)
![](https://i-blog.csdnimg.cn/blog_migrate/1dc5e9b6a1964dea09606d3bc6e3b9f6.png)
![](https://i-blog.csdnimg.cn/blog_migrate/01284c94da51a82ec4cda6d676f664e8.png)
![](https://i-blog.csdnimg.cn/blog_migrate/565c436d99f171940398ed71f37b7ac1.png)
![](https://i-blog.csdnimg.cn/blog_migrate/99e000b8ab2865de6d2a6cd6842afd74.png)
![](https://i-blog.csdnimg.cn/blog_migrate/01284c94da51a82ec4cda6d676f664e8.png)
![](https://i-blog.csdnimg.cn/blog_migrate/fb7aec3a4a4cb89e8a18b133415dd79f.png)
![](https://i-blog.csdnimg.cn/blog_migrate/fb7aec3a4a4cb89e8a18b133415dd79f.png)
![](https://i-blog.csdnimg.cn/blog_migrate/bdb1cf17b24c43bffda2556745129fc3.png)
![](https://i-blog.csdnimg.cn/blog_migrate/6a1aa10093e08590dd997e63dd762744.png)
![](https://i-blog.csdnimg.cn/blog_migrate/fb7aec3a4a4cb89e8a18b133415dd79f.png)
![](https://i-blog.csdnimg.cn/blog_migrate/fb7aec3a4a4cb89e8a18b133415dd79f.png)
![](https://i-blog.csdnimg.cn/blog_migrate/3a1cc904c68ea707987f0b10e241a13e.png)
![](https://i-blog.csdnimg.cn/blog_migrate/75f2d25dfbb5a8fd3f74b30401e052ba.png)
![](https://i-blog.csdnimg.cn/blog_migrate/565c436d99f171940398ed71f37b7ac1.png)
![](https://i-blog.csdnimg.cn/blog_migrate/fb7aec3a4a4cb89e8a18b133415dd79f.png)
![](https://i-blog.csdnimg.cn/blog_migrate/fb7aec3a4a4cb89e8a18b133415dd79f.png)
![](https://i-blog.csdnimg.cn/blog_migrate/bdb1cf17b24c43bffda2556745129fc3.png)
![](https://i-blog.csdnimg.cn/blog_migrate/6a1aa10093e08590dd997e63dd762744.png)
![](https://i-blog.csdnimg.cn/blog_migrate/fb7aec3a4a4cb89e8a18b133415dd79f.png)
![](https://i-blog.csdnimg.cn/blog_migrate/fb7aec3a4a4cb89e8a18b133415dd79f.png)
![](https://i-blog.csdnimg.cn/blog_migrate/3a1cc904c68ea707987f0b10e241a13e.png)
![](https://i-blog.csdnimg.cn/blog_migrate/7f99f5b2410652854b6ae51a0303268e.png)
![](https://i-blog.csdnimg.cn/blog_migrate/565c436d99f171940398ed71f37b7ac1.png)
![](https://i-blog.csdnimg.cn/blog_migrate/fb7aec3a4a4cb89e8a18b133415dd79f.png)
![](https://i-blog.csdnimg.cn/blog_migrate/fb7aec3a4a4cb89e8a18b133415dd79f.png)
![](https://i-blog.csdnimg.cn/blog_migrate/bdb1cf17b24c43bffda2556745129fc3.png)
![](https://i-blog.csdnimg.cn/blog_migrate/6a1aa10093e08590dd997e63dd762744.png)
![](https://i-blog.csdnimg.cn/blog_migrate/fb7aec3a4a4cb89e8a18b133415dd79f.png)
![](https://i-blog.csdnimg.cn/blog_migrate/fb7aec3a4a4cb89e8a18b133415dd79f.png)
![](https://i-blog.csdnimg.cn/blog_migrate/3a1cc904c68ea707987f0b10e241a13e.png)
![](https://i-blog.csdnimg.cn/blog_migrate/09a1716417c2ebf1a4d9139670042bda.png)
![](https://i-blog.csdnimg.cn/blog_migrate/565c436d99f171940398ed71f37b7ac1.png)
4,从尾部遍历
向量,进行如下操作:
5,对
向量进行一阶差分运算,如同步骤2,得到
。
6,遍历得到的差分向量
,如果
,则
为投影向量
的一个峰值位,对应的峰值为
;如果
,则
为投影向量
的一个波谷位,对应的波谷为
。
下面我们来结合一个实际的向量值,给中中间结合的计算。
1,
。
它的曲线图像如下把示,图中红色圈标出了曲线的峰值,而绿字圈标出了图像的波谷位置。
2,计算
的一阶差分,我们得到
。
3,对
进行取符号运算,得到向量
。
4,对
作一次遍历,如步骤4。
。
5,对
做一阶差分,得到向量
。
6,遍历向量
,我们就得到了两个峰值点和一个波谷点。
算法原理
其实上述算法的核心思路非常简单,曲线的峰值点,满足一阶导数为0,并且满足二阶导数为负;而波谷点,则满足一阶导数为0,二阶导数为正。
在上面的算法里面,我们首先计算了一阶的导数
,然后我们将其符号化,是因为我们并不关心一阶导数的大小。
然后我们去看那些一阶层数为0的地方,我们发现,那些平台上的点,有些并不是波峰与波谷,然后很多处在上坡与下坡的路上,所以我们将它们的一阶导数设为与它们所在的坡面梯度方向相同。
最后我们再来计算二阶导数时,就会发现只要为2或者-2,所以曲线斜在这个点发生了变化,由正变负或由负变正。找到这些点,也就找到了原曲线中的波峰或波谷点。
#include<opencv2\highgui\highgui.hpp>
#include<opencv2\imgproc\imgproc.hpp>
#include<iostream>
#include<stdio.h>
#include<vector>
using namespace cv;
using namespace std;
void findpeak(Mat &srcImage, vector<int>& resultVec)
{
Mat verMat;
Mat resMat = srcImage.clone();
int thresh = 130;
int threshType = 0;
const int maxVal = 255;
threshold(srcImage, srcImage, thresh, maxVal, threshType);
srcImage.convertTo(srcImage, CV_32FC1);
reduce(srcImage, verMat, 0, CV_REDUCE_SUM);
float* iptr = (float*)verMat.data;
vector<int> tempVec(verMat.cols - 1, 0);
for (int i = 0; i < verMat.cols - 1; ++i, ++iptr)
{
if(*(iptr+1) - *iptr > 0)
{
tempVec[i] = 1;
}
else if (*(iptr + 1) - *iptr < 0)
{
tempVec[i] = -1;
}
else
{
tempVec[i] = 0;
}
}
for (int i = tempVec.size() - 1; i >= 0; i--)
{
if (tempVec[i] == 0 && i == tempVec.size() - 1)
{
tempVec[i] = 1;
}
else if (tempVec[i] == 0)
{
if (tempVec[i + 1] >= 0)
tempVec[i] = 1;
else
tempVec[i] = -1;
}
}
for (vector<int>::size_type i = 0; i != tempVec.size() - 1; i++)
{
if (tempVec[i + 1] - tempVec[i] == -2)
{
resultVec.push_back(i + 1);
}
}
for (int i = 0; i < resultVec.size(); i++)
{
cout << resultVec[i] << endl;
for (int ii = 0; ii < resMat.rows; ++ii)
{
resMat.at<uchar>(ii, resultVec[i]) = 255;
}
}
imshow("resMat", resMat);
}
int main()
{
Mat image = imread("test.jpg");
if (!image.data)
{
printf("could not load image...\n");
return -1;
}
cvtColor(image, image, CV_BGR2GRAY);
imshow("image", image);
vector<int> resultVec;
findpeak(image, resultVec);
waitKey(0);
return 0;
}