読者です 読者をやめる 読者になる 読者になる

FFTでウェーブレット変換する

はじめに

フーリエ変換のように周波数の特性を得ることができる連続ウェーブレット変換を試しました。

連続ウェーブレット変換

母関数によっていろいろな種類がありますが、一番よく目にするのはGabor のウェーブレットです。こちらのサイトによると、(ガボールウェーブレット変換(VC++)
f:id:wildpie:20141029000131p:plain
を評価するみたいです。場所とスケールを変えた関数との類似度を計算していて、場所に応じた周波数の特性を求めることができます。フーリエ変換ですと、この場所が周波数高くてここは低いなどの議論はできませんが、ウェーブレット変換ならオッケーです。

実装

前の記事(EigenでFFTする - wildpieの日記)でFFTを使えるようになったので、コンボリューションは周波数領域で行います。周波数領域だとかけ算になるやつです。
たとえばガウシアンフィルタだとこんな感じです(たぶん)。

void CalcGaussKernel(VectorXd& kernel)
{
    const double sigma = 1.0;
    for (int t = 0; t < kernel.size(); t++)
    {
        kernel[t] = exp(-t*t/(2.0 * sigma))  / (sqrt(2.0*M_PI) * sigma);
    }
}

void GaussianFilter(const VectorXd& data, VectorXd& result)
{
    VectorXd kernel(data.size());
    CalcGaussKernel(kernel);

    VectorXcd data_freq = fft.fwd(data);
    VectorXcd kernel_freq = fft.fwd(kernel);
    VectorXcd product = data_freq.array() * kernel_freq.array();
    result = fft.inv(product);
}

これのKernelをGaborに変えます。

void CalcGaborKernel(VectorXcd& kernel, double a)
{
    const double sigma = 1.0;
    for (int i = 0; i < kernel.size(); i++)
    {
        double t = i / a / 8000.0;
        double omega = 2.0 * M_PI;
        kernel[i] = exp(-t*t/(sigma * sigma)) / (2.0 * sqrt(M_PI) * sigma)
                          * exp(std::complex<double>(0.0, omega * t)); // 共役?
    }
}

VectorXcd data_freq = fft.fwd(data);
MatrixXcd map(16, data.size());
for (int m = 1; m <= map.rows(); m++)
{
    double f = 2.0 * m; // 周波数, 0はどうしよう
    double a = 1.0 / f;
    VectorXcd kernel(data.size());
    CalcGaborKernel(kernel, a);

    VectorXcd kernel_freq = fft.fwd(kernel);

    VectorXcd product = data_freq.array() * kernel_freq.array();
    VectorXcd result = fft.inv(product);
    map.row(m - 1) = result / sqrt(a);
}

mapを表示するGUIを作りました。マイク音声をリアルタイムに処理しています
c#でマイク音声をFFTする - wildpieの日記)。
合っているかあやしいですが、周波数の高い場所を検出できています。
f:id:wildpie:20141029003421p:plain