Я написал этот код для сглаживания кривой.
Он берет 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;
}
}
}
Но я получаю очень высокие значения для каждой точки вместо значений, аналогичных предыдущей. Форма максимально увеличена, что не так в этом алгоритме?
в следующем блоке:
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
}
То, что вы видите здесь, это басовая реализация фильтра с конечной импульсной характеристикой (FIR), который реализует функция окна вагона. Думая о проблеме с точки зрения DSP, вам нужно отфильтровать входящие vector
с 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;
}
}
Увеличьте число коэффициентов фильтра, используя эту строку, чтобы увидеть постепенно более сглаженный результат. С одним коэффициентом фильтра сглаживание отсутствует.
boxFIR box(1);
Код достаточно гибок, что вы можете даже изменить форму окна, если хотите. Сделайте это, изменив коэффициенты, определенные в конструкторе.
Примечание. Это даст немного другой результат вашей реализации, так как это причинный фильтр (зависит только от текущей выборки и предыдущих выборок). Ваша реализация не является причинно-следственной, так как она рассчитывает на будущее для будущих выборок, чтобы получить среднее значение, и поэтому вам нужны условные операторы для ситуации, когда вы приближаетесь к концу вектора. Если вы хотите, чтобы вывод был похож на то, что вы пытаетесь сделать с вашим фильтром, используя этот алгоритм, запустите ваш вектор по этому алгоритму в обратном порядке (это работает нормально, пока оконная функция симметрична). Таким образом, вы можете получить похожий вывод без неприятной условной части алгоритма.
Фильтрация хороша для сглаживания памяти. Это обратный проход для learnvst-х ответ, чтобы предотвратить фазовые искажения:
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: http://www.mathworks.com/help/signal/ref/filtfilt.html
Текущее значение точки используется дважды: один раз, потому что вы используете +=
и один раз, если y==0
, Таким образом, вы строите сумму, например, из 6 баллов, но делите ее только на 5. Эта проблема касается как IF, так и ELSE. Также: вы должны проверить, что вектор достаточно длинный, иначе ваш ELSE-регистр будет читаться с отрицательными индексами.
Само по себе следующее — это не проблема, а лишь мысль: рассматривали ли вы вопрос об использовании алгоритма, который касается только каждой точки дважды ?: Вы можете сохранить временное значение xy (инициализированное, чтобы быть идентичным первому пункту), а затем при посещении В каждой точке вы просто добавляете новую точку и вычитаете самую старую точку, если она дальше, чем ваша NEIGHBOURS
назад. Вы сохраняете эту «промежуточную сумму» обновленной для каждой точки и сохраняете это значение, деленное на NEIGHBOURS
-номер в новую точку.
Вы делаете сложение с самой точкой, когда вам нужно взять соседние точки — просто сместите индекс на 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
}