我正在尝试在 iOS 中实现真正的 FFT,因为我使用的是 Accelerate Framework。这是我的 Swift 代码。
class FFT {
private var fftSetup: FFTSetup?
private var log2n: Float?
private var length: Int?
func initialize(count: Int){
length = count
log2n = log2(Float(length!))
self.fftSetup = vDSP_create_fftsetup(vDSP_Length(log2n!), FFTRadix(kFFTRadix2))!
}
func computeFFT(input: [Float]) -> ([Float], [Float]) {
var real = input
var imag = [Float](repeating: 0.0, count: input.count)
var splitComplexBuffer = DSPSplitComplex(realp: &real, imagp: &imag)
let halfLength = (input.count/2) + 1
real = [Float](repeating: 0.0, count: halfLength)
imag = [Float](repeating: 0.0, count: halfLength)
// input is alternated across the real and imaginary arrays of the DSPSplitComplex structure
splitComplexBuffer = DSPSplitComplex(fromInputArray: input, realParts: &real, imaginaryParts: &imag)
// even though there are 2 real and 2 imaginary output elements, we still need to ask the fft to process 4 input samples
vDSP_fft_zrip(fftSetup!, &splitComplexBuffer, 1, vDSP_Length(log2n!), FFTDirection(FFT_FORWARD))
// zrip results are 2x the standard FFT and need to be scaled
var scaleFactor = Float(1.0/2.0)
vDSP_vsmul(splitComplexBuffer.realp, 1, &scaleFactor, splitComplexBuffer.realp, 1, vDSP_Length(halfLength))
vDSP_vsmul(splitComplexBuffer.imagp, 1, &scaleFactor, splitComplexBuffer.imagp, 1, vDSP_Length(halfLength))
return (real, imag)
}
func computeIFFT(real: [Float], imag: [Float]) -> [Float]{
var real = [Float](real)
var imag = [Float](imag)
var result : [Float] = [Float](repeating: 0.0, count: length!)
var resultAsComplex : UnsafeMutablePointer<DSPComplex>? = nil
result.withUnsafeMutableBytes {
resultAsComplex = $0.baseAddress?.bindMemory(to: DSPComplex.self, capacity: 512)
}
var splitComplexBuffer = DSPSplitComplex(realp: &real, imagp: &imag)
vDSP_fft_zrip(fftSetup!, &splitComplexBuffer, 1, vDSP_Length(log2n!), FFTDirection(FFT_INVERSE));
vDSP_ztoc(&splitComplexBuffer, 1, resultAsComplex!, 2, vDSP_Length(length! / 2));
//
//// Neither the forward nor inverse FFT does any scaling. Here we compensate for that.
var scale : Float = 1.0/Float(length!);
var copyOfResult = result;
vDSP_vsmul(&result, 1, &scale, ©OfResult, 1, vDSP_Length(length!));
result = copyOfResult
return result
}
func deinitialize(){
vDSP_destroy_fftsetup(fftSetup)
}
}这是我用于计算 rFFT 和 irFFT 的 Python 代码
# calculate fft of input block
in_block_fft = np.fft.rfft(np.squeeze(in_buffer)).astype("complex64")
# apply mask and calculate the ifft
estimated_block = np.fft.irfft(in_block_fft * out_mask)
问题:swift
如果我为 512 帧计算 rFFT 并将 irFFT 应用于 rFFT 的结果,我会得到相同的原始数组。
python
python也是如此,如果我采用rFFT和irFFT,我会得到原始数组作为返回。
问题
如果我比较 就会出现问题Swift rFFT 和 Python rFFT 的结果 .他们的结果是不同的十进制值。有时实部相同,但虚部完全不同。
我在 Python 中尝试了不同的框架,例如 Numpy、SciPy 和 TensorFlow 并且它们的结果完全相同(小数部分略有不同)。但是当我使用上面的 Swift 代码在相同的 iOS 输入上计算 rfft 时,结果是不同的。
如果任何有 Accelerate Framework 经验并掌握 FFT 知识的人帮助我解决这个问题,那将非常有帮助。我对FFT的了解有限。
最佳答案
两个观察结果:首先您拨打 DSPSplitComplex(realp: &real, imagp: &imag)
创建临时指针和 fromInputArray
初始值设定项已弃用。看看Data Packing for Fourier Transforms最佳实践。
其次,bindMemory
容量是元素的数量。下面几行创建了 512 个复杂元素,它们应该(如果我理解正确的话)创建 256 个复杂元素来表示 512 个真实元素。
result.withUnsafeMutableBytes {
resultAsComplex = $0.baseAddress?.bindMemory(to: DSPComplex.self, capacity: 512)
}
西蒙
关于python - Swift vDSP rFFT 与 Python np.fft.rfft() 不同,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65234054/