计算FFT相关系数

5
我希望使用AForge 2.2.5计算2个声音样本的相关系数。 我从这里阅读了计算交叉相关的公式。 并且,我从这里阅读了计算相关系数的公式。 这是我目前拥有的:
在调用CrossCorrelation()之前,已经执行了FFT。
static Complex[] CrossCorrelation(Complex[] ffta, Complex[] fftb)
{
    var conj = ffta.Select(i => new Complex(i.Re, -i.Im)).ToArray();

    for (int a = 0; a < conj.Length; a++)
        conj[a] = Complex.Multiply(conj[a], fftb[a]);

    FourierTransform.FFT(conj, FourierTransform.Direction.Backward);

    return conj;
}

static double CorrelationCoefficient(Complex[] ffta, Complex[] fftb)
{
    var correlation = CrossCorrelation(ffta, fftb);
    var a = CrossCorrelation(ffta, ffta);
    var b = CrossCorrelation(fftb, fftb);

    // Not sure if this part is correct..
    var numerator = correlation.Select(i => i.SquaredMagnitude).Max();
    var denominatora = a.Select(i => i.Magnitude).Max();
    var denominatorb = b.Select(i => i.Magnitude).Max();

    return numerator / (denominatora * denominatorb);
}

我不确定这是否是正确的实现函数(或处理数据)的方式,因为我对DSP非常陌生。如果有人能指引我走向正确的方向,我将非常感激。


3
你是否尝试使用测试数据?它是否返回了期望的结果?为每个函数编写一些单元测试,传递特定的输入并将实际结果与期望值进行比较。你不需要传递整个声音文件,只需创建一些可以手动计算(或使用另一个程序)的小数组即可。 - Panagiotis Kanavos
我同意Panagiotis Kanavos的观点。例如,您知道两个相同信号之间的相关性将给出1的相关性。 - Amitay Nachmani
我认为在返回结果之前,您需要进行平方根运算(给定第二个链接公式),这里您有平方系数。 - Antoine Bergamaschi
1个回答

5

使用FFT和Afrog进行交叉相关:

  • 用零填充信号: 根据AForge fft的文档: 该方法仅接受2n大小的数据数组,其中n可能在[1,14]范围内变化。

因此,您需要确保输入大小正确填充到2的幂长度,并处于指定范围内: 考虑到至少有一半的波形是“空白”(零)

参考:

https://dsp.stackexchange.com/questions/741/why-should-i-zero-pad-a-signal-before-taking-the-fourier-transform

https://dsp.stackexchange.com/questions/1919/efficiently-calculating-autocorrelation-using-ffts

  • 对两个信号进行FFT
  • 将一个信号乘以另一个信号的共轭(逐元素相乘)
  • 执行反FFT
  • 将最大值作为相关系数并将其索引作为延迟(信号滞后)

为什么要选择结果IFFT的最大值:

来自维基百科,交叉相关对于确定两个信号之间的时间延迟非常有用,例如用于确定声学信号在麦克风阵列中传播的时间延迟。2[3][需要澄清] 计算两个信号之间的交叉相关后,交叉相关函数的最大值(如果信号为负相关,则为最小值)指示信号最佳对齐的时间点,即两个信号之间的时间延迟由最大值的参数或交叉相关的arg max确定,如下所示:

参考: https://math.stackexchange.com/questions/1080709/why-is-the-maximum-value-of-cross-correlation-achieved-at-similar-section

根据上述要点,交叉计算使用以下代码计算:

  //same as OP
  public Complex[] CrossCorrelation(Complex[] ffta, Complex[] fftb)
  {
    var conj = ffta.Select(i => new Complex(i.Re, -i.Im)).ToArray();

    conj = conj.Zip(fftb, (v1, v2) => Complex.Multiply(v1, v2)).ToArray();
    FourierTransform.FFT(conj, FourierTransform.Direction.Backward);

    //get that data and plot in Excel, to show the max peak 
    Console.WriteLine("---------rr[i]---------");
    double[] rr = new double[conj.Length];
    rr = conj.Select(i => i.Magnitude).ToArray();

    for (int i = 0; i < conj.Length; i++)
        Console.WriteLine(rr[i]);

    Console.WriteLine("----end-----");
    return conj;
  } 

 //tuble.Item1: Cor. Coefficient
 //tuble.Item2: Delay of signal (Lag)
 public Tuple<double, int> CorrelationCoefficient(Complex[] ffta, Complex[] fftb)
{
    Tuple<double, int> tuble;
    var correlation = CrossCorrelation(ffta, fftb);
    var seq = correlation.Select(i => i.Magnitude);
    var maxCoeff = seq.Max();

    int maxIndex = seq.ToList().IndexOf(maxCoeff);
    Console.WriteLine("max: {0}", maxIndex);
    tuble = new Tuple<double, int>(maxCoeff, maxIndex);
    return tuble;
}
  // Pad signal with zeros up to 2^n and convert to complex 
  public List<Complex> ToComplexWithPadding(List<double> sample, int padding = 1)
    {
        //As per AForge documentation:
        //    The method accepts data array of 2n size only, where n may vary in the [1, 14] range
        //So you would need to make sure the input size is correctly padded to a length that is a power of 2, and in the specified range:

        double logLength = Math.Ceiling(Math.Log(sample.Count * padding, 2.0));
        int paddedLength = (int)Math.Pow(2.0, Math.Min(Math.Max(1.0, logLength), 14.0));
        Complex[] complex = new Complex[paddedLength];
        var samples = sample.ToArray();
        // copy all input samples
        int i = 0;
        for (; i < sample.Count; i++)
        {
            complex[i] = new Complex(samples[i], 0);
            Console.WriteLine(complex[i].Re);

        }
        // pad with zeros
        for (; i < paddedLength; i++)
        {
            complex[i] = new Complex(0, 0);
            Console.WriteLine(complex[i].Re);
        }
        return complex.ToList();

    }

    // extra for signal generation for testing. You can find in the link of the life demo.

您可以运行具有延迟11生成的两个信号样本的实时演示,并且结果与信号的实际延迟匹配。

具有两个生成信号的实时演示

输出结果:

 correlation Coef: 0.33867796353274 | Delay: 11| actual delay: 11

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接