在C#中实现无第三方库的2D图像互相关,结果与Matlab不符求助
嘿,我来帮你排查下这个问题——用FFT实现互相关时,和Matlab结果不一致是个很常见的坑,我之前也踩过好几次!咱们从几个关键方向逐一排查:
1. 零填充:线性互相关vs循环互相关
Matlab的xcorr2默认做的是线性互相关,它会自动把两个图像零填充到 图像高度之和-1、图像宽度之和-1 的尺寸,这样就不会出现循环互相关的“缠绕”效应。但你的代码直接对原始尺寸的图像做FFT,得到的是循环互相关,这会导致结果的尺寸和峰值位置完全不对。
解决办法:先给两个信号做零填充,再执行FFT操作:
// 计算需要的目标尺寸 int targetHeight = signal.Height + pulse.Height - 1; int targetWidth = signal.Width + pulse.Width - 1; // 把原始图像零填充到目标尺寸(建议把原始图像放在中心,和Matlab行为对齐) Signal2D paddedSignal = PadToTargetSize(signal, targetHeight, targetWidth); Signal2D paddedPulse = PadToTargetSize(pulse, targetHeight, targetWidth); // 再执行互相关计算 return InverseFFT2D(FFT2D(paddedSignal) * FFT2D(paddedPulse).GetConjugate());
这里的PadToTargetSize需要实现为在图像周围填充0,比如如果原始图像是20x20,目标是30x30,就在上下左右各填5个0,让原始图像居中。
2. IFFT的归一化处理
Matlab的ifft2会自动给结果做归一化(除以总像素数 height * width),但很多自定义的IFFT实现会漏掉这一步,导致你的结果幅值是Matlab的N倍,看起来完全不对。
检查你的InverseFFT2D函数,一定要在最后加上归一化:
// 在InverseFFT2D的最后,对每个元素除以总像素数 int totalPixels = result.Height * result.Width; for (int i = 0; i < result.Height; i++) { for (int j = 0; j < result.Width; j++) { // 如果是复数信号,实部和虚部分别除以totalPixels result[i,j] = new Complex(result[i,j].Real / totalPixels, result[i,j].Imaginary / totalPixels); // 如果是实数信号,直接除以totalPixels即可 } }
3. 共轭计算是否正确
你的GetConjugate()方法必须正确实现复数共轭:对于复数a + bi,共轭是a - bi。如果这里写错了,互相关的相位会完全颠倒,结果肯定和Matlab对不上。
可以做个小测试:在Matlab里计算conj(fft2(template))的几个点,再对比C#里FFT2D(pulse).GetConjugate()的对应值,看看是否一致。
4. FFT的移位对齐
Matlab的xcorr2结果的峰值位置,和直接用FFT计算的结果可能因为原点位置不同而偏移。你可以在C#里对IFFT后的结果做一次“移位”操作,把峰值移到中心,和Matlab的结果对齐——就像Matlab里的fftshift函数那样:
// 对互相关结果做fftshift,对齐峰值位置 Signal2D shiftedResult = FftShift(result);
FftShift的实现是把图像的四个象限互换:比如把左上角象限和右下角交换,右上角和左下角交换。
5. 数据类型精度问题
如果你的Signal2D用的是单精度float而不是双精度double,可能会有精度损失,导致和Matlab的双精度计算结果有偏差。建议先改成double测试,排除这个问题。
验证小技巧
你可以先在Matlab里手动用FFT计算互相关,对比和xcorr2的差异,确认你的C#代码的计算路径是否和Matlab一致:
% 加载你的图像和模板 signal = imread('your_image.png'); template = imread('your_template.png'); % 手动零填充 target_h = size(signal,1) + size(template,1) - 1; target_w = size(signal,2) + size(template,2) - 1; signal_pad = padarray(signal, [target_h - size(signal,1), target_w - size(signal,2)], 0, 'both'); template_pad = padarray(template, [target_h - size(template,1), target_w - size(template,2)], 0, 'both'); % 用FFT计算互相关 fft_signal = fft2(signal_pad); fft_template = fft2(template_pad); result_fft = ifft2(fft_signal .* conj(fft_template)); result_fft = real(result_fft); % 互相关结果应为实数,取实部 % 和xcorr2对比 result_xcorr = xcorr2(signal, template); max(abs(result_fft - result_xcorr), [], 'all') % 这个值应该接近0,说明路径正确
如果Matlab里手动FFT的结果和xcorr2一致,那你的C#代码只要对齐这个流程,就能得到正确结果。最后别忘了检查你的FFT2D和InverseFFT2D是否正确处理了行和列的FFT(先对每行做FFT,再对每列做FFT,顺序不影响结果,但要和Matlab一致)。
内容的提问来源于stack exchange,提问作者Tyler Atkinson

