我写了这段代码来平滑曲线。 它在一个点旁边需要5个点并添加它们并对其进行平均。
/* Smoothing */
void smoothing(vector<Point2D> &a)
{
//How many neighbours to smooth
int NO_OF_NEIGHBOURS=10;
vector<Point2D> tmp=a;
for(int i=0;i<a.size();i++)
{
if(i+NO_OF_NEIGHBOURS+1<a.size())
{
for(int j=1;j<NO_OF_NEIGHBOURS;j++)
{
a.at(i).x+=a.at(i+j).x;
a.at(i).y+=a.at(i+j).y;
}
a.at(i).x/=NO_OF_NEIGHBOURS;
a.at(i).y/=NO_OF_NEIGHBOURS;
}
else
{
for(int j=1;j<NO_OF_NEIGHBOURS;j++)
{
a.at(i).x+=tmp.at(i-j).x;
a.at(i).y+=tmp.at(i-j).y;
}
a.at(i).x/=NO_OF_NEIGHBOURS;
a.at(i).y/=NO_OF_NEIGHBOURS;
}
}
}
但是我为每个点获得了非常高的值,而不是与前一点相似的值。形状最大化了很多,这个算法出了什么问题?
答案 0 :(得分:10)
你在这里看到的是实现boxcar window function的有限脉冲响应(FIR)滤波器的低音实现。考虑到DSP方面的问题,您需要使用vector
相等的FIR系数过滤传入的NO_OF_NEIGHBOURS
,每个系数的值均为1/NO_OF_NEIGHBOURS
。通常最好使用既定算法而不是重新发明轮子。
这是一个非常笨拙的实现,我快速敲定了过滤器加倍。您可以轻松修改此选项以过滤数据类型。该演示显示了上升锯功能(0,.25,.5,1)的几个周期的过滤,仅用于演示目的。它编译,所以你可以玩它。
#include <iostream>
#include <vector>
using namespace std;
class boxFIR
{
int numCoeffs; //MUST be > 0
vector<double> b; //Filter coefficients
vector<double> m; //Filter memories
public:
boxFIR(int _numCoeffs) :
numCoeffs(_numCoeffs)
{
if (numCoeffs<1)
numCoeffs = 1; //Must be > 0 or bad stuff happens
double val = 1./numCoeffs;
for (int ii=0; ii<numCoeffs; ++ii) {
b.push_back(val);
m.push_back(0.);
}
}
void filter(vector<double> &a)
{
double output;
for (int nn=0; nn<a.size(); ++nn)
{
//Apply smoothing filter to signal
output = 0;
m[0] = a[nn];
for (int ii=0; ii<numCoeffs; ++ii) {
output+=b[ii]*m[ii];
}
//Reshuffle memories
for (int ii = numCoeffs-1; ii!=0; --ii) {
m[ii] = m[ii-1];
}
a[nn] = output;
}
}
};
int main(int argc, const char * argv[])
{
boxFIR box(1); //If this is 1, then no filtering happens, use bigger ints for more smoothing
//Make a rising saw function for demo
vector<double> a;
a.push_back(0.); a.push_back(0.25); a.push_back(0.5); a.push_back(0.75); a.push_back(1.);
a.push_back(0.); a.push_back(0.25); a.push_back(0.5); a.push_back(0.75); a.push_back(1.);
a.push_back(0.); a.push_back(0.25); a.push_back(0.5); a.push_back(0.75); a.push_back(1.);
a.push_back(0.); a.push_back(0.25); a.push_back(0.5); a.push_back(0.75); a.push_back(1.);
box.filter(a);
for (int nn=0; nn<a.size(); ++nn)
{
cout << a[nn] << endl;
}
}
使用此行增加滤波器系数的数量,以查看逐渐更平滑的输出。只有1个滤波器系数,没有平滑。
boxFIR box(1);
代码非常灵活,您甚至可以根据需要更改窗口形状。通过修改构造函数中定义的系数来完成此操作。
注意:这将为您的实现提供略有不同的输出,因为这是一个因果过滤器(仅取决于当前样本和以前的样本)。您的实现不是因果关系,因为它在未来的样本中及时展望以获得平均值,这就是为什么您需要条件语句来处理接近矢量末尾的情况。如果你想要输出就像你试图用你的过滤器一样使用这个算法,那么通过这个算法反向运行你的向量(只要窗口函数是对称的,这就可以正常工作)。这样你就可以得到类似的输出而没有讨厌的条件部分算法。
答案 1 :(得分:3)
在以下块中:
for(int j=0;j<NO_OF_NEIGHBOURS;j++)
{
a.at(i).x=a.at(i).x+a.at(i+j).x;
a.at(i).y=a.at(i).y+a.at(i+j).y;
}
对于每个邻居,您将a.at(i)的x和y分别添加到邻居值。
我理解正确,它应该是这样的。
for(int j=0;j<NO_OF_NEIGHBOURS;j++)
{
a.at(i).x += a.at(i+j+1).x
a.at(i).y += a.at(i+j+1).y
}
答案 2 :(得分:3)
过滤有助于'记忆'平滑。这是learnvst's答案的反向传递,以防止phase distortion:
for (int i = a.size(); i > 0; --i)
{
// Apply smoothing filter to signal
output = 0;
m[m.size() - 1] = a[i - 1];
for (int j = numCoeffs; j > 0; --j)
output += b[j - 1] * m[j - 1];
// Reshuffle memories
for (int j = 0; j != numCoeffs; ++j)
m[j] = m[j + 1];
a[i - 1] = output;
}
关于MATLAB中的零相位失真FIR滤波器的更多信息:http://www.mathworks.com/help/signal/ref/filtfilt.html
答案 3 :(得分:1)
该点的当前值使用两次:一次是因为您使用+=
而一次是y==0
。所以你要构建例如6个点的总和,但只能除以5.这个问题出现在IF和ELSE的情况下。另外:你应该检查向量是否足够长,否则你的ELSE案例将以负指数读取。
以下本身不是问题,只是一个想法:您是否考虑使用仅触及每个点两次的算法?:您可以存储临时xy值(初始化为与第一个点相同),然后当您访问每个点时,您只需添加新点,如果它超过NEIGHBOURS
,则减去最旧的点。保持每个点的“运行总和”更新并将此值除以NEIGHBOURS
- 数字存储到新点。
答案 4 :(得分:1)
当您需要获取邻居点时,您可以使用点本身进行添加 - 只需将索引偏移1:
for(int j=0;j<NO_OF_NEIGHBOURS;j++)
{
a.at(i).x += a.at(i+j+1).x
a.at(i).y += a.at(i+j+1).y
}
答案 5 :(得分:0)
这对我来说很好:
for (i = 0; i < lenInput; i++)
{
float x = 0;
for (int j = -neighbours; j <= neighbours; j++)
{
x += input[(i + j <= 0) || (i + j >= lenInput) ? i : i + j];
}
output[i] = x / (neighbours * 2 + 1);
}