6

曲線を平滑化するためにこのコードを作成しました。ポイントの隣に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;
        }

    }

}

しかし、前のポイントと同様の値ではなく、各ポイントで非常に高い値を取得します。形状が大幅に最大化されていますが、このアルゴリズムの何が問題になっていますか?

4

6 に答える 6

10

ここにあるように見えるのは、ボックスカーウィンドウ関数を実装する有限インパルス応答(FIR)フィルターの低音の実装です。DSPの観点から問題を考えると、それぞれがの値をvector持つNO_OF_NEIGHBOURS等しいFIR係数で着信をフィルタリングする必要があります1/NO_OF_NEIGHBOURS。通常、車輪の再発明ではなく、確立されたアルゴリズムを使用するのが最善です。

これは、フィルターが2倍になる、かなりだらしない実装です。これを簡単に変更して、データ型をフィルタリングできます。デモでは、デモンストレーションの目的で、立ち上がりソー関数(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);

コードは十分に柔軟なので、必要に応じてウィンドウの形状を変更することもできます。これを行うには、コンストラクターで定義された係数を変更します。

注:これは因果的フィルターであるため、実装に対してわずかに異なる出力が得られます(現在のサンプルと以前のサンプルにのみ依存します)。実装は、平均を作成するために将来のサンプルを先読みするため、因果関係はありません。そのため、ベクトルの終わりに近づいている状況では、条件付きステートメントが必要です。このアルゴリズムを使用してフィルターで実行しようとしているような出力が必要な場合は、このアルゴリズムを逆に実行してベクトルを実行します(これは、ウィンドウ関数が対称である限り正常に機能します)。そうすれば、アルゴリズムの厄介な条件付き部分なしで同様の出力を得ることができます。

于 2012-10-19T12:01:13.383 に答える
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)のsxと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
            }
于 2012-10-19T11:34:19.780 に答える
3

フィルタリングは、「メモリ」の平滑化に適しています。これは、位相歪みを防ぐための、 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のゼロ位相歪みFIRフィルターの詳細:http://www.mathworks.com/help/signal/ref/filtfilt.html

于 2015-04-07T14:20:59.870 に答える
1

ポイントの現在の値は2回使用されます。1回はを使用するため+=、もう1回はを使用するためですy==0。したがって、たとえば6ポイントの合計を作成していますが、5で割るだけです。この問題はIFとELSEの両方の場合に発生します。また、ベクトルが十分に長いことを確認する必要があります。そうしないと、ELSEの場合は負のインデックスで読み取られます。

以下はそれ自体は問題ではありませんが、考えてみてください。すべてのポイントに2回だけ触れるアルゴリズムを使用することを検討しましたか?:一時的なxy値(最初のポイントと同じになるように初期化されます)を保存してから、各ポイントに新しいポイントを追加し、後ろよりも遠い場合は最も古いポイントを減算しますNEIGHBOURS。この「ランニングサム」をポイントごとに更新し、この値をNEIGHBOURS-numberで割った値を新しいポイントに保存します。

于 2012-10-19T11:42:22.467 に答える
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
 }
于 2012-10-19T12:05:06.953 に答える
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);
}
于 2019-06-09T14:24:32.983 に答える