不确定我做错了什么。我从Accelerate框架得到的结果对我来说似乎不对。任何帮助将不胜感激!
以下是一些比较AForge和vDPS的图表 这是我运行的vDSP代码
fftSetup = vDSP_create_fftsetup( 16, 2);
// Convert the data into a DSPSplitComplex
int samples = spectrumDataSize;
int samplesOver2 = samples/2;
DSPSplitComplex * complexData = new DSPSplitComplex;
float *realpart = (float *)calloc(samplesOver2, sizeof(float));
float *imagpart = (float *)calloc(samplesOver2, sizeof(float));
complexData->realp = realpart;
complexData->imagp = imagpart;
vDSP_ctoz((DSPComplex *)realData, 2, complexData, 1,samplesOver2);
// Calculate the FFT
// ( I'm assuming here you've already called vDSP_create_fftsetup() )
vDSP_fft_zrip(fftSetup, complexData, 1, log2f(samples), FFT_FORWARD);
// Scale the data
//float scale = (float) FFT_SCALE; //scale is 32
vDSP_vsmul(complexData->realp, 1, &scale, complexData->realp, 1,samplesOver2);
vDSP_vsmul(complexData->imagp, 1, &scale, complexData->imagp, 1, samplesOver2);
vDSP_zvabs(complexData, 1, spectrumData, 1, samples);
free(complexData->realp);
free(complexData->imagp);
delete complexData;
// All done!
return spectrumData;
这就是我在AForge中所做的事情
foreach (float f in floatData)
{
if (i >= this.fft.Length)
break;
this.fft[i++] = new Complex(f * fftSize, 0);
}
AForge.Math.FourierTransform.FFT(this.fft, FourierTransform.Direction.Forward);
答案 0 :(得分:2)
以下子程序
vDSP_ctoz((DSPComplex *)realData, 2, complexData, 1,samplesOver2);
已执行,complexData
有samplesOver2
个元素。但不久之后,你打电话给
vDSP_zvabs(complexData, 1, spectrumData, 1, samples);
期望complexData
有samples
个元素,即两倍。这不可能。
另外,realData
如何布局?我问,因为vDSP_ctoz
期望它的第一个参数以
real0, imag0, real1, imag1, ... real(n-1), imag(n-1).
如果您的数据确实是真实的,那么imag0, imag1, ... imag(n-1)
应该都是0.如果不是,那么vDSP_ctoz
可能不会这样。 (除非你以一种聪明的方式包装真实数据,这将是两个[原文如此]聪明的一半!)
最后,vDSP_create_fftsetup( 16, 2);
应该更改为
vDSP_create_fftsetup(16, 0);
=============================================== ====================
我的示例代码附在postscript中:
FFTSetup fftSetup = vDSP_create_fftsetup(
16, // vDSP_Length __vDSP_log2n,
kFFTRadix2 // FFTRadix __vDSP_radix
// CAUTION: kFFTRadix2 is an enum that is equal to 0
// kFFTRadix5 is an enum that is equal to 2
// DO NOT USE 2 IF YOU MEAN kFFTRadix2
);
NSAssert(fftSetup != NULL, @"vDSP_create_fftsetup() failed to allocate storage");
int numSamples = 65536; // numSamples must be an integer power of 2; in this case 65536 = 2 ^ 16
float realData[numSamples];
// Prepare the real data with (ahem) fake data, in this case
// the sum of 3 sinusoidal waves representing a C major chord.
// The fake data is rigged to have a sampling frequency of 44100 Hz (as for a CD).
// As always, the Nyquist frequency is just half the sampling frequency, i.e., 22050 Hz.
for (int i = 0; i < numSamples; i++)
{
realData[i] = sin(2 * M_PI * 261.76300048828125 * i / 44100.0) // C4 = 261.626 Hz
+ sin(2 * M_PI * 329.72717285156250 * i / 44100.0) // E4 = 329.628 Hz
+ sin(2 * M_PI * 392.30804443359375 * i / 44100.0); // G4 = 391.995 Hz
}
float splitReal[numSamples / 2];
float splitImag[numSamples / 2];
DSPSplitComplex splitComplex;
splitComplex.realp = splitReal;
splitComplex.imagp = splitImag;
vDSP_ctoz(
(const DSPComplex *)realData, // const DSPComplex __vDSP_C[],
2, // vDSP_Stride __vDSP_strideC, MUST BE A MULTIPLE OF 2
&splitComplex, // DSPSplitComplex *__vDSP_Z,
1, // vDSP_Stride __vDSP_strideZ,
(numSamples / 2) // vDSP_Length __vDSP_size
);
vDSP_fft_zrip(
fftSetup, // FFTSetup __vDSP_setup,
&splitComplex, // DSPSplitComplex *__vDSP_ioData,
1, // vDSP_Stride __vDSP_stride,
(vDSP_Length)lround(log2(numSamples)), // vDSP_Length __vDSP_log2n,
// IMPORTANT: THE PRECEDING ARGUMENT MUST BE LOG_BASE_2 OF THE NUMBER OF floats IN splitComplex
// FOR OUR EXAMPLE, THIS WOULD BE (numSamples / 2) + (numSamples / 2) = numSamples
kFFTDirection_Forward // FFTDirection __vDSP_direction
);
printf("DC component = %f\n", splitComplex.realp[0]);
printf("Nyquist component = %f\n\n", splitComplex.imagp[0]);
// Next, we compute the Power Spectral Density (PSD) from the FFT.
// (The PSD is just the magnitude-squared of the FFT.)
// (We don't bother with scaling as we are only interested in relative values of the PSD.)
float powerSpectralDensity[(numSamples / 2) + 1]; // the "+ 1" is to make room for the Nyquist component
// We move the Nyquist component out of splitComplex.imagp[0] and place it
// at the end of the array powerSpectralDensity, squaring it as we go:
powerSpectralDensity[numSamples / 2] = splitComplex.imagp[0] * splitComplex.imagp[0];
// We can now zero out splitComplex.imagp[0] since the imaginary part of the DC component is, in fact, zero:
splitComplex.imagp[0] = 0.0;
// Finally, we compute the squares of the magnitudes of the elements of the FFT:
vDSP_zvmags(
&splitComplex, // DSPSplitComplex *__vDSP_A,
1, // vDSP_Stride __vDSP_I,
powerSpectralDensity, // float *__vDSP_C,
1, // vDSP_Stride __vDSP_K,
(numSamples / 2) // vDSP_Length __vDSP_N
);
// We print out a table of the PSD as a function of frequency
// Replace the "< 600" in the for-loop below with "<= (numSamples / 2)" if you want
// the entire spectrum up to and including the Nyquist frequency:
printf("Frequency_in_Hz Power_Spectral_Density\n");
for (int i = 0; i < 600; i++)
{
printf("%f, %f\n", (i / (float)(numSamples / 2)) * 22050.0, powerSpectralDensity[i]);
// Recall that the array index i = 0 corresponds to zero frequency
// and that i = (numSamples / 2) corresponds to the Nyquist frequency of 22050 Hz.
// Frequency values intermediate between these two limits are scaled proportionally (linearly).
}
// The output PSD should be zero everywhere except at the three frequencies
// corresponding to the C major triad. It should be something like this:
/***************************************************************************
DC component = -0.000000
Nyquist component = -0.000000
Frequency_in_Hz Power_Spectral_Density
0.000000, 0.000000
0.672913, 0.000000
1.345825, 0.000000
2.018738, 0.000000
2.691650, 0.000000
.
.
.
260.417175, 0.000000
261.090088, 0.000000
261.763000, 4294967296.000000
262.435913, 0.000000
263.108826, 0.000000
.
.
.
328.381348, 0.000000
329.054260, 0.000000
329.727173, 4294967296.000000
330.400085, 0.000000
331.072998, 0.000000
.
.
.
390.962219, 0.000000
391.635132, 0.000000
392.308044, 4294966784.000000
392.980957, 0.000000
393.653870, 0.000000
.
.
.
***************************************************************************/
vDSP_destroy_fftsetup(fftSetup);
答案 1 :(得分:2)
这一行:
vDSP_zvabs(complexData, 1, spectrumData, 1, samples);
应该是:
float cr = complexData->realp[0], ci = complexData->imagp[0];
vDSP_zvabs(complexData, 1, spectrumData, 1, samplesOver2);
spectrumData[0] = cr*cr;
spectrumData[samplesOver2] = ci*ci; // See remarks below.
这是因为N个样本的实数到复数FFT返回N / 2 + 1个结果。其中两个结果是实数,它们被打包到complexData-&gt; realp [0]和complexData-&gt; imagp [0]中。剩余的N / 2-1结果是复数,通常与complexData-&gt; realp [i]中的实数分量和complexData-&gt; imagp [i]中的虚数分量一起存储,0 <&lt;我&lt; N / 2。
vDSP_zvabs计算复数的大小,除了第一个输出(在spectrumData [0]中)由于将两个数字打包到[0]元素中而不正确。用cr * cr覆盖spectrumData [0]来纠正它。如果为此提供了空间,您还可以将其他压缩元素(奈奎斯特频率)的幅度写入spectrumData [samplesOver2]。
其他一些说明:
spectrumDataSize必须是2的幂。
将基数2对数计算为log2f(样本)并不是理想的做法。我认为我们(Apple)已经使log2f返回两个整数幂的完全正确的结果,但是应该避免取决于浮点精确性,除非注意非常确定它。
无需动态分配带有“new”的DSPSplitComplex。它是仅包含两个指针的POD(普通旧数据),因此您可以简单地声明“DSPSplitComplex complexData”并将其用作结构,而不是指向结构的指针。
答案 2 :(得分:0)
一些想法......
int numberOfInputSamples = ..;
int numberOfInputSamplesOver2 = numberOfInputSamples/2;
fftSetup = vDSP_create_fftsetup( log2(numberOfInputSamples), FFT_RADIX2 );
...
Float32 scale = (Float32) 1.0 / (2 * numberOfInputSamples);
...
float *spectrumData = (float *)calloc( numberOfInputSamplesOver2, sizeof(float));
vDSP_zvabs( complexData, 1, spectrumData, 1, numberOfInputSamplesOver2 );
所以最后你会有numberOfInputSamplesOver2浮点数,对吗?
(从技术上讲,它是numberOfInputSamplesOver2 + 1,但整个包装是另一个问题)
答案 3 :(得分:0)
您似乎计算一个长度为N / 2的FFT,另一个长度为N.因此,不同长度FFT的结果不同。
答案 4 :(得分:0)
我假设你打电话给vDSP_ctoz
,你的数据不是偶数。如果是这种情况,你还需要在fft之后解压缩。
调用实数FFT的应用程序可能必须使用两个转换函数,一个在FFT调用之前,另一个在之后。如果输入数组不是奇偶分割配置,则需要这样做。
希望有所帮助。
答案 5 :(得分:0)
我对AForge或Accelerate一点也不熟悉,但在另一个处理2D图像的项目中升级FFT库时遇到了一些问题,这看起来与你的相似。
事实证明,来自FFT库的输出数据表示并不是唯一的,对于某些应用,如果“交换”,输出数据会更加方便,以便将低频放在中心而不是角落。
如果您在FFT算法http://www.eso.org/sci/software/eclipse/eug/eug/man/fft.html上查看此页面,您会注意到两种格式都受支持,并且描述了交换结构(在底部)。
在我看来,你在右边绘制的数据看起来更像是左边的数据,你是要在中心周围交换(镜像)数据阵列的右半部分。