Python 如何缩放基于FFT的互相关,使其峰值等于Pearson';s rho 问题的描述

Python 如何缩放基于FFT的互相关,使其峰值等于Pearson';s rho 问题的描述,python,numpy,image-processing,signal-processing,fft,Python,Numpy,Image Processing,Signal Processing,Fft,FFT可用于计算两个信号或图像之间的互相关。要确定两个信号A和B之间的延迟或滞后,只需确定以下峰值: IFFT(FFT(A)*共轭(FFT(B))) 然而,峰值的振幅与单个信号的频谱振幅有关。因此,为了确定峰值,该峰值的振幅必须根据两个信号中的总能量进行缩放 这样做的一个方法是。这给出了rho的合理近似值,特别是当样本之间的延迟很小,但不是精确值时 我认为产生这种错误的原因是皮尔逊相关性仅定义为信号的重叠部分,而归一化因子(两个自相关峰的几何平均值)包括非重叠部分的贡献。我考虑了两种方法来解

FFT可用于计算两个信号或图像之间的互相关。要确定两个信号AB之间的延迟或滞后,只需确定以下峰值:

IFFT(FFT(A)*共轭(FFT(B)))

然而,峰值的振幅与单个信号的频谱振幅有关。因此,为了确定峰值,该峰值的振幅必须根据两个信号中的总能量进行缩放

这样做的一个方法是。这给出了rho的合理近似值,特别是当样本之间的延迟很小,但不是精确值时

我认为产生这种错误的原因是皮尔逊相关性仅定义为信号的重叠部分,而归一化因子(两个自相关峰的几何平均值)包括非重叠部分的贡献。我考虑了两种方法来解决这个问题,并通过FFT生成rho的精确值。在第一个例子中(下面称为
rho_-exact_1
),我将样本裁剪到它们的重叠部分,并根据这些部分计算归一化因子。在第二个例子中(下面称为
rho_-exact_2
),我计算了信号重叠部分中包含的测量分数,并将全自相关归一化因子乘以该分数

两个都不行!下图显示了使用基于DFT的互相关计算Pearson's rho的三种方法的曲线图。仅显示了互相关峰的区域。每个估计值都接近正确值1.0,但不等于它

下面是我用来执行计算的代码。我使用了一个简单的正弦波作为示例信号。我注意到,如果我使用方波(占空比不一定是50%),方法的误差会改变

有人能解释一下发生了什么事吗

一个有效的例子
将numpy导入为np
从matplotlib导入pyplot作为plt
#制作一个带有256个点的时间向量
#和一个源信号
N_周期=10.0
N_点=256.0
t=np.arange(0,N个循环*np.pi,np.pi*N个循环/N个点)
信号=np.sin(t)
使用_rect=False
如果直接使用:
阈值=-0.75
信号[np.式中(信号>=阈值)]=1.0
信号[np.其中(信号号//2:
偏移估计=偏移估计-N
#对于Pearson的近似估计
#相关性,我们通过RMS标准化
#单个自相关系数:
自相关函数=np.abs(np.fft.ifft(np.fft.fft(sample_1)*np.fft.fft(sample_1.conjugate())
自相关函数_2=np.abs(np.fft.ifft(np.fft.fft(sample_2)*np.fft.fft(sample_2.conjugate())
xc_denom_近似=np.sqrt(np.max(自相关_1))*np.sqrt(np.max(自相关_2))
rho_近似值=xc_num/xc_denom_近似值
打印“rho_近似值”,np.最大值(rho_近似值)
#这是一个近似值,因为我们
#包括整个样本的自相关
#而不仅仅是重叠部分;
#使用裁剪版本的样本应该
#产生正确的相关性:
样本_1_裁剪=样本_1[偏移:]
样本_2_裁剪=样本_2[:-偏移]
#这些向量应该是相同的:
assert np.all(示例1\u裁剪==示例2\u裁剪)
#计算裁剪样本的自相关
#和相应的rho值
自相关系数=np.abs(np.fft.ifft(np.fft.fft(sample_1_裁剪)*np.fft.fft(sample_1_裁剪).conjugate())
自相关函数被裁剪=np.abs(np.fft.ifft(np.fft.fft(样本被裁剪)*np.fft.fft(样本被裁剪).conjugate())
xc_denom_exact_1=np.sqrt(np.max(自相关_1_裁剪))*np.sqrt(np.max(自相关_2_裁剪))
rho_-exact_1=xc_num/xc_-denom_-exact_1
打印'rho_-exact_1',np.max(rho_-exact_1)
#或者,我们可以尝试使用
#全样本自相关和
#按用于缩放的像素数进行缩放
#计算分子:
缩放系数=浮点(len(样本1\u裁剪))/浮点(len(样本1))
rho_精确_2=xc_num/(xc_denom_近似*比例系数)
打印'rho_-exact_2',np.max(rho_-exact_2)
#最后是一个合理性检查:rho实际上是1.0吗
#对于这两个信号:
rho_corrcoef=np.corrcoef(样本_1_裁剪,样本_2_裁剪)[0,1]
打印“rho_corrcoef”,rho_corrcoef
x=np.arange(len(rho_近似))
plt.绘图(x,rho_近似值,label='FFT rho_近似值')
plt.plot(x,rho_精确图1,label='FFT rho_精确图1')
plt.plot(x,rho_-exact_2,label='FFT rho_-exact_2')
plt.plot(x,np.one(len(x))*rho_corrcoef,'k--',label='Pearson rho')
plt.legend()
plt.ylim(.75,1.25))
plt.xlim((0,20))
plt.show()

两个N周期离散信号F和G之间的归一化互相关定义为:

由于分子是两个向量(F和G_x)之间的点积,分母是这两个向量范数的乘积,因此标量r_x必须介于-1和+1之间,并且是向量之间角度的余弦(参见)。如果向量F和G_x对齐,则r_x=1。如果r_x=1,则向量F和G_x对齐,因为存在三角形不等式。为确保这些特性,分子处的向量必须与分母处的向量匹配

使用离散傅里叶变换可以一次计算所有分子。事实上,该变换将卷积转换为傅里叶空间中的逐点积。这就是为什么在您执行的测试中,不同估计的归一化互相关不是1


  • 对于第一次测试“近似值”,
    sample_1
    sample_2
    都是从周期信号中提取的。两者的长度相同,但长度不是周期的倍数,因为它是2.5个周期(5pi)(下图).因此,由于dft执行相关性时,就好像它们与周期信号一样,因此发现
    sample_1
    sample_2
    不是完全相关的,并且r_xI意识到这里的误差很小,但对于我感兴趣的问题(volumet
    import numpy as np
    from matplotlib import pyplot as plt
    
    # make a time vector w/ 256 points
    # and a source signal
    N_cycles = 10.0
    N_points = 256.0
    
    t = np.arange(0,N_cycles*np.pi,np.pi*N_cycles/N_points)
    signal = np.sin(t)
    
    use_rect = False
    if use_rect:
        threshold = -0.75
        signal[np.where(signal>=threshold)]=1.0
        signal[np.where(signal<threshold)]=-1.0
    
    # normalize the signal (not technically
    # necessary for this example, but required
    # for measuring correlation of physically
    # different signals)
    signal = signal/signal.std()
    
    # generate two samples of the signal
    # with a temporal offset:
    N = 128
    offset = 5
    sample_1 = signal[:N]
    sample_2 = signal[offset:N+offset]
    
    # determine the offset through cross-
    # correlation
    xc_num = np.abs(np.fft.ifft(np.fft.fft(sample_1)*np.fft.fft(sample_2).conjugate()))
    offset_estimate = np.argmax(xc_num)
    if offset_estimate>N//2:
        offset_estimate = offset_estimate - N
    
    
    # for an approximate estimate of Pearson's
    # correlation, we normalize by the RMS
    # of individual autocorrelations:
    autocorrelation_1 = np.abs(np.fft.ifft(np.fft.fft(sample_1)*np.fft.fft(sample_1).conjugate()))
    autocorrelation_2 = np.abs(np.fft.ifft(np.fft.fft(sample_2)*np.fft.fft(sample_2).conjugate()))
    xc_denom_approx = np.sqrt(np.max(autocorrelation_1))*np.sqrt(np.max(autocorrelation_2))
    rho_approx = xc_num/xc_denom_approx
    print 'rho_approx',np.max(rho_approx)
    
    # this is an approximation because we've
    # included autocorrelation of the whole samples
    # instead of just the overlapping portion;
    # using cropped versions of the samples should
    # yield the correct correlation:
    sample_1_cropped = sample_1[offset:]
    sample_2_cropped = sample_2[:-offset]
    
    # these should be identical vectors:
    assert np.all(sample_1_cropped==sample_2_cropped)
    
    # compute autocorrelations of cropped samples
    # and corresponding value for rho
    autocorrelation_1_cropped = np.abs(np.fft.ifft(np.fft.fft(sample_1_cropped)*np.fft.fft(sample_1_cropped).conjugate()))
    autocorrelation_2_cropped = np.abs(np.fft.ifft(np.fft.fft(sample_2_cropped)*np.fft.fft(sample_2_cropped).conjugate()))
    xc_denom_exact_1 = np.sqrt(np.max(autocorrelation_1_cropped))*np.sqrt(np.max(autocorrelation_2_cropped))
    rho_exact_1 = xc_num/xc_denom_exact_1
    print 'rho_exact_1',np.max(rho_exact_1)
    
    # alternatively we could try to use the
    # whole sample autocorrelations and just
    # scale by the number of pixels used to
    # compute the numerator:
    scaling_factor = float(len(sample_1_cropped))/float(len(sample_1))
    rho_exact_2 = xc_num/(xc_denom_approx*scaling_factor)
    print 'rho_exact_2',np.max(rho_exact_2)
    
    # finally a sanity check: is rho actually 1.0
    # for the two signals:
    rho_corrcoef = np.corrcoef(sample_1_cropped,sample_2_cropped)[0,1]
    print 'rho_corrcoef',rho_corrcoef
    
    x = np.arange(len(rho_approx))
    plt.plot(x,rho_approx,label='FFT rho_approx')
    plt.plot(x,rho_exact_1,label='FFT rho_exact_1')
    plt.plot(x,rho_exact_2,label='FFT rho_exact_2')
    plt.plot(x,np.ones(len(x))*rho_corrcoef,'k--',label='Pearson rho')
    plt.legend()
    plt.ylim((.75,1.25))
    plt.xlim((0,20))
    plt.show()
    
    xc_num_cropped = np.abs(np.fft.ifft(np.fft.fft(sample_1_cropped)*np.fft.fft(sample_2_cropped).conjugate()))
    autocorrelation_1_cropped = np.abs(np.fft.ifft(np.fft.fft(sample_1_cropped)*np.fft.fft(sample_1_cropped).conjugate()))
    autocorrelation_2_cropped = np.abs(np.fft.ifft(np.fft.fft(sample_2_cropped)*np.fft.fft(sample_2_cropped).conjugate()))
    xc_denom_exact_11 = np.sqrt(np.max(autocorrelation_1_cropped))*np.sqrt(np.max(autocorrelation_2_cropped))
    rho_exact_11 = xc_num_cropped/xc_denom_exact_11
    print 'rho_exact_11',np.max(rho_exact_11)