与MATLAB实现相比,当我尝试使用Accelerate中的vDSP_conv()进行卷积时,我得到了不一致的结果。在使用此函数计算卷积时,有几个关于奇怪结果的StackOverflow帖子,但据我所知,我正在使用框架,并结合了其他Stack Overflow帖子的建议。这是我的代码:
public func conv(x: [Float], k: [Float]) -> [Float] {
let resultSize = x.count + k.count - 1
var result = [Float](count: resultSize, repeatedValue: 0)
let kEnd = UnsafePointer<Float>(k).advancedBy(k.count - 1)
let xPad: [Float] = [Float](count: (2*k.count)+1, repeatedValue: 0.0)
let xPadded = x + xPad
vDSP_conv(xPadded, 1, kEnd, -1, &result, 1, vDSP_Length(resultSize), vDSP_Length(k.count))
}
据我所知,我正在按照Accelerate框架文档here
中的说明进行正确的零填充我定义了两个测试数组A: [Float] = [0, 0, 1, 0, 0]
和B: [float] = [1, 0, 0]
。
在MATLAB中,当我运行conv(A, B)
时,我得到[0, 0, 1, 0, 0, 0, 0]
。
但是,当我运行上述vDSP conv()
时,我得到[1, 0, 0, 0, 0, 0, 0]
。
我的实施有什么问题?我已经多次查看过,并查看了我能找到的所有SO帖子,但仍然无法解释这种不一致性。
除此之外,有没有一种更有效的方法来填充阵列然后我在这里?为了保持x
不可变,我创建了新的xPadded
数组,但毫无疑问,这是一种更有效的执行此填充的方法。
**编辑**
正如Martin R所建议的,我在数组的开头和结尾平均填充k.count -1
,如下所示。
public func conv(x: [Float], k: [Float]) -> [Float] {
let resultSize = x.count + k.count - 1
var result = [Float](count: resultSize, repeatedValue: 0)
let kEnd = UnsafePointer<Float>(k).advancedBy(k.count - 1)
let xPad: [Float] = [Float](count: k.count-1, repeatedValue: 0.0)
let xPadded = xPad + x + xPad
vDSP_conv(xPadded, 1, kEnd, -1, &result, 1, vDSP_Length(resultSize), vDSP_Length(k.count))
return result
}
使用此代码,conv(A, B)
仍会返回[1,0,0,0,0,0,0]。
我正在调用如下所示的函数:
let A: [Float] = [0, 0, 1, 0, 0]
let B: [Float] = [1, 0, 0]
let C: [Float] = conv(A, k: B)
答案 0 :(得分:3)
对于长度为A
和B
的两个数组m
和n
,
Accelerate框架中的vDSP_conv()
函数计算一个长度为m - n + 1
的新数组。
这对应于具有形状的MATLAB函数conv()
的结果
参数设置为“有效”:
仅在没有零填充边缘的情况下计算的卷积部分。 ...
获得与MATLAB的“完整”卷积相同的结果
你必须在开头和结尾用A
元素填充n-1
数组,这样就得到一个长度为m + n - 1
的结果数组。
适用于您的职能:
let xPad = Repeat(count: k.count - 1, repeatedValue: Float(0.0))
let xPadded = xPad + x + xPad
使用Repeat()
可能稍微更高效,因为它
创建序列而不是数组。但最终,一个新阵列
必须创建为vDSP_conv()
函数的参数,
所以没有太大的改进空间。
答案 1 :(得分:1)
@MartinR我弄清楚为什么我的代码不适用于Arrays。我在一个使用Surge作为链接框架的项目中编写此代码。 Surge会使[Float]和[Double]数组的+
运算符重载,以便它成为元素元素的元素添加。因此,当我执行x + xPad
时,它没有按预期扩展数组的大小,只是返回x
,因为xPad
仅包含零。但是,Surge没有为序列重载+
运算符,因此使用Repeat()
成功扩展了数组。谢谢你的帮助 - 永远不会想到尝试序列!
答案 2 :(得分:1)
对下一个陷入困境的可怜的灵魂的一些澄清: Apple提供some sample code关于如何使用vDSP_conv但它没用。实际上,这让我感到困惑,因为该代码中的注释表示需要填充输入缓冲区而不指定实际输入样本的放置位置:
下面定义的SignalLength用于分配空间,它是过滤器长度四舍五入到四个元素的倍数并添加到结果长度。
SignalLength = (FilterLength+3 & -4u) + ResultLength;
因此,上面的公式给出的长度(大于){x}是k.count - 1的xPad + x + xPad
不同。
重要的是在填充缓冲区中复制输入(信号)样本的位置:它必须位于k.count - 1
。
因此,以上接受的解决方案有效。但是如果你相信苹果公司的例子中的评论(BTW没有出现在官方文档中)那么你可以做出妥协:使用他们的公式(上面的SignalLength)来计算和分配填充缓冲区(它会有点更大)并使用k.count - 1
(即滤波器长度 - 1)作为信号的起始偏移量(在本例中为x)。我做了这个,结果现在匹配ippsConvolve_32f和Matlab。
(对不起,这应该是评论,但我没有足够的声誉)。