Java中不同长度信号的交叉相关

时间:2017-03-04 14:24:31

标签: java matlab signal-processing audio-processing cross-correlation

我试图在java中基于properties(第五个用FFT变换)实现计数互相关。它应该适用于不同长度的信号。我使用普林斯顿大学的简单FFT库(FFT.javaComplex.java)。独立于目标信号,我在第三指数处接收互相关峰值。对于下面的示例,它应该放在0索引处,因为两个信号在前三个位置是相同的。我使该解决方案有可能同步两个不同大小的音频文件(一个是第二个文件的一部分)。

@Test
public void find_synchro_by_cross_correlation() {
    double[] source = {1, 2, 3, 4, 5, 6, 7, 8};
    double[] target = {1, 2, 3, 0, 0, 0, 0, 0}; //signal padded 0's
    int n = source.length;
    //creating complex arrays
    Complex[] sourceComplex = new Complex[n];
    Complex[] targetComplex = new Complex[n];
    for (int i = 0; i < n; i++) {
        sourceComplex[i] = new Complex(source[i], 0);
        targetComplex[i] = new Complex(target[i], 0);
    }
    //counting FFT of both signals
    Complex[] fftS = FFT.fft(sourceComplex);
    Complex[] fftT = FFT.fft(targetComplex);
    //conjugate the first one
    for (int i = 0; i < fftS.length; i++) {
        fftS[i] = fftS[i].conjugate();
    }
    // point-wise multiply
    Complex[] timeProduct = new Complex[fftS.length];
    for (int i = 0; i < fftS.length; i++) {
        timeProduct[i] = fftS[i].times(fftT[i]);
    }
    // compute inverse FFT
    Complex[] y = FFT.ifft(timeProduct);

    System.out.println("id:\txcorr abs:\t\t\txcorr complexes:");
    for (int i = 0; i < y.length; i++) {
        Complex c = y[i];
        System.out.println(i + "\t" +c.abs() + "\t\t\t" + c.toString());
    }
    System.out.println("Max arg: " + argmax(y));
    Assert.assertEquals(0, DSP.argmax(y)); //I assume that the peak should be at 0 index
}

public static int argmax(Complex[] a)
    {
        double y = Double.MIN_VALUE;
        int idx = -1;

        for(int x = 0; x < a.length; x++)
        {
            if(a[x].abs() > y)
            {
                y = a[x].abs();
                idx = x;
            }
        }

        return idx;
    }

调用结果:

id: xcorr abs:          xcorr complexes:
0   14.0            14.0 + 5.551115123125783E-16i
1   16.0            16.0 + 1.133107779529596E-15i
2   26.0            26.0 - 8.881784197001252E-16i
3   44.0            44.0 - 2.021286199229721E-15i
4   38.0            38.0 - 5.551115123125783E-16i
5   32.0            32.0 - 6.432490598706545E-16i
6   26.0            26.0 + 8.881784197001252E-16i
7   20.0            20.0 + 1.5314274795707798E-15i
Max arg: 3 //I thought it should equals 0

对于参考/调试目的,这里有一个gist,其中包含下载完整代码的来源。

matlab / octave代码可以正常工作(但它仍然没有使用FFT算法):

pkg load signal
x = [2 3 4];
y = 1:1:10;

nx = length(x);
ny = length(y);
cc = nan(1,ny-nx+1);
for ii = 0 : ny-nx
  id = (1:nx) + ii;
  cc(ii+1) = sum(x.*y(id))/(sqrt(sum(x.^2)*sum(y(id).^2)));
end
[ccmx,idmx] = max(cc) 

对于这个问题还有另一个解决方案,但是它仍需要很长时间,解决方案的复杂性是n ^ 2 lgn

Y = [2 3 4];
X = 1:1:10;
lngX = length(X);
lngY = length(Y);
assert(lngX >= lngY);
lags = 0:(lngX-lngY);
for i = lags
   c(i+1) = xcorr(X(i+1:i+lngY) - mean(X(i+1:i+lngY)), Y - mean(Y),0,'coeff');
end
[m,i]=max(c);
printf('max=%f, lag=%d\n',c(i),lags(i));
plot(lags,c,'-',lags(i),c(i),'*r');

更新

我找到了一种解决方案,但我对其复杂性并不满意。我重写了最后一个matlab示例。我只计算一次较短信号的FFT以及系数的一部分(因为效率)。我计算每个可能滞后的0偏移(系数数组中的第一个元素)的相关性。此计算的峰值显示已滞后的信号量。如果您想在长度不同于n = power2的阵列上计算FFT,则必须更改FFT库。您可以使用FFTW java包装器或JTransforms。您可以在下面阅读代码。

@Test
public void find_synchro_by_circular_convolution() {
    double[] source = {1, 2, 3, 4, 5, 6, 7, 8};
    double[] target = {4, 5, 6, 7};
    int n = target.length;
    int lags = source.length - n + 1;
    //normalize(target);
    Complex[] targetComplex = convertToComplex(target);
    double sumsqTarget = sumsq(target);
    Complex[] fftTarget = FFT.fft(targetComplex);

    Complex[] c = new Complex[lags];
    for (int i = 0; i < lags; i++) {
        //double[] shrinkedSource = normalize(source, i, n);
        double[] shrinkedSource = new double[n];
        shrinkArray(source, shrinkedSource, i, n);
        Complex[] sourceComplex = convertToComplex(shrinkedSource);

        //cross correlation coefficient
        Complex[] fftSource = FFT.fft(sourceComplex);
        conjugate(fftSource);
        multiplyComplexXInPlace(fftSource, fftTarget);
        Complex[] y = FFT.ifft(fftSource);
        double rms = rms(sumsqTarget, shrinkedSource);
        c[i] = y[0].scale(rms);
    }

    System.out.println("id:\txcorr abs:\t\t\txcorr complexes:");
    for (int i = 0; i < lags; i++) {
        Complex ci = c[i];
        System.out.println(i + "\t" +ci.abs() + "\t\t\t" + ci.toString());
    }
    System.out.println("Max arg: " + DSP.argmax(c));
}

@NotNull
private Complex[] convertToComplex(double[] target) {
    int n = target.length;
    Complex[] targetComplex = new Complex[n];
    for (int i = 0; i < n; i++) {
        targetComplex[i] = new Complex(target[i], 0);
    }
    return targetComplex;
}

private void multiplyComplexXInPlace(Complex[] x, Complex[] y) {
    for (int j = 0; j < x.length; j++) {
        x[j] = x[j].times(y[j]);
    }
}

private void conjugate(Complex[] fftSource) {
    for (int j = 0; j < fftSource.length; j++) {
        fftSource[j] = fftSource[j].conjugate();
    }
}

private double[] normalize(double[] source, int i, int n) {
    double[] normalized = new double[n];
    shrinkArray(source, normalized, i, n);
    normalize(normalized);
    return normalized;
}

private void normalize(double[] target) {
    double meanTarget = mean(target);
    for (int i = 0; i < target.length; i++) {
        target[i] = target[i] - meanTarget;
    }
}

private void shrinkArray(double[] source, double[] destination, int startPosition, int length) {
    System.arraycopy(source, startPosition, destination, 0, length);
}

private double mean(double[] x) {
    double sum = 0;
    int n = x.length;
    if(n != 0) {
        for (int i = 0; i < n; i++) {
            sum += x[i];
        }
        return sum / x.length;
    }
    return sum;
}

private double rms(double cached_sumsq_x, float[] y) {
    double sumsq_x = cached_sumsq_x;
    double sumsq_y = sumsq(y);
    return 1/Math.sqrt(sumsq_x*sumsq_y);
}

private double rms(double cached_sumsq_x, double[] y) {
    double sumsq_x = cached_sumsq_x;
    double sumsq_y = sumsq(y);
    return 1/Math.sqrt(sumsq_x*sumsq_y);
}

private double rms(float[] x, float[] y) {
    double sumsq_x = sumsq(x);
    double sumsq_y = sumsq(y);
    return 1/Math.sqrt(sumsq_x*sumsq_y);
}

private double sumsq(float[] x) {
    double sumsq = 0.0;
    int n = x.length;
    for (int i = 0; i < n; i++) {
        sumsq += x[i]*x[i];
    }
    return sumsq;
}

private double sumsq(double[] x) {
    double sumsq = 0.0;
    int n = x.length;
    for (int i = 0; i < n; i++) {
        sumsq += x[i]*x[i];
    }
    return sumsq;
}

代码的结果:

id: xcorr abs:          xcorr complexes:
0   0.9759000729485332          0.9759000729485332
1   0.9941037221861875          0.9941037221861875
2   0.9990767240206739          0.9990767240206739
3   1.0         1.0
4   0.9995437747834998          0.9995437747834998
Max arg: 3

但是......来吧。简单的线性溶胶更快!在频域中计算它真的很难吗?

import static java.lang.Math.abs;

public class LinearSynchronisation {

    public long findSynchroSamples(Float[] longFile, Float[] shortfile) {
        double lowestDifference = Double.MAX_VALUE;
        long maxCorrelationSampleNumer = 0;
        for (int j = 0; j < longFile.length - shortfile.length; j++) {
            double diff = getCorrelation(longFile, shortfile, j);
            if (diff < lowestDifference) {
                maxCorrelationSampleNumer = j;
                lowestDifference = diff;
            }
        }
        return maxCorrelationSampleNumer;
    }

    private double getCorrelation(Float[] longFile, Float[] shortFile, int start) {
        double diff = 0;
        for (int i = 0; i < shortFile.length; i++) {
            diff += abs(longFile[start+i] - shortFile[i]);
        }
        return diff;
    }

    public static void main(String[] args) {
        Float[] source = {1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f};
        Float[] target = {4.0f, 5.0f, 6.0f, 7.0f};

        LinearSynchronisation ls = new LinearSynchronisation();
        long synchroSamples = ls.findSynchroSamples(source, target);

        System.out.println(synchroSamples);
    }

}

0 个答案:

没有答案