我想执行一个简单的 2D 图像卷积,但我的内核是均匀大小的。我应该为我的内核中心选择哪些索引?我尝试在谷歌上搜索答案并查看现有代码。人们通常将他们的内核居中,因此在新的 0 之前会有一个样本。所以,如果我们有一个 4x4 内核,居中的索引应该是-2 -1 0 +1
. 那是对的吗?如果是,为什么会这样?有人可以解释为什么-2 -1 0 +1
是正确的而-1 0 +1 +2
不是正确的吗?请记住,我想在不使用 FFT 的情况下执行卷积。
3 回答
如果我正确理解了您的问题,那么对于即使大小的内核,您是正确的,将内核居中是惯例,以便在新零之前还有一个样本。
因此,对于宽度为 4 的内核,居中的索引将-2 -1 0 +1
如您上面所说。
然而,这实际上只是一个约定——无论如何都很少使用不对称卷积,并且不对称的确切性质(左/右等)与“正确”结果无关。我想大多数实现都以这种方式运行的原因是,它们可以在给定相同输入的情况下给出可比较的结果。
在频域中执行卷积时,无论如何都会填充内核以匹配图像大小,并且您已经声明您正在空间域中执行卷积。
我对为什么首先需要使用均匀大小的内核更感兴趣。
正确的答案是返回左上角的结果像素,无论您的矩阵大小是否均匀。然后你可以简单地在一个简单的扫描线中执行操作,它们不需要内存。
private static void applyBlur(int[] pixels, int stride) {
int v0, v1, v2, r, g, b;
int pos;
pos = 0;
try {
while (true) {
v0 = pixels[pos];
v1 = pixels[pos+1];
v2 = pixels[pos+2];
r = ((v0 >> 16) & 0xFF) + ((v1 >> 16) & 0xFF) + ((v2 >> 16) & 0xFF);
g = ((v0 >> 8 ) & 0xFF) + ((v1 >> 8) & 0xFF) + ((v2 >> 8) & 0xFF);
b = ((v0 ) & 0xFF) + ((v1 ) & 0xFF) + ((v2 ) & 0xFF);
r/=3;
g/=3;
b/=3;
pixels[pos++] = r << 16 | g << 8 | b;
}
}
catch (ArrayIndexOutOfBoundsException e) { }
pos = 0;
try {
while (true) {
v0 = pixels[pos];
v1 = pixels[pos+stride];
v2 = pixels[pos+stride+stride];
r = ((v0 >> 16) & 0xFF) + ((v1 >> 16) & 0xFF) + ((v2 >> 16) & 0xFF);
g = ((v0 >> 8 ) & 0xFF) + ((v1 >> 8) & 0xFF) + ((v2 >> 8) & 0xFF);
b = ((v0 ) & 0xFF) + ((v1 ) & 0xFF) + ((v2 ) & 0xFF);
r/=3;
g/=3;
b/=3;
pixels[pos++] = r << 16 | g << 8 | b;
}
}
catch (ArrayIndexOutOfBoundsException e) { }
}
在对偶数卷积及其在时间卷积网络中的应用进行了一些思考之后,我决定,下面的实验将为在 tensorflow/keras 中对偶数卷积进行居中给出答案:
import keras
import numpy as np
import tensorflow as tf
import keras.backend as K
import keras.layers as layers
from keras.layers import Conv2D, Input
from keras.initializers import Constant
if __name__ == '__main__':
inputs = Input(shape=(None,1,1))
even_conv = Conv2D(1,(4,1),padding="same",
kernel_initializer=Constant(value=1.),use_bias=False)(inputs)
f = K.function(inputs=[inputs],outputs=[even_conv])
test_input = np.arange(10)[np.newaxis,...,np.newaxis,np.newaxis].astype(np.float)
result = f(inputs=[test_input])[0]
print(np.squeeze(test_input))
# [0. 1. 2. 3. 4. 5. 6. 7. 8. 9.]
print(np.squeeze(result))
# [ 3. 6. 10. 14. 18. 22. 26. 30. 24. 17.]
正如您所看到的“相同”填充输入数组在开头填充了 1 个零,最后填充了 2 个零:[0. 0. 1. 2. 3. 4. 5. 6. 7. 8. 9. 0. 0.]
。因此对于 tensorflow,即使大小的内核中心将遵循 4-kernel:-1 0 +1 +2
和2*n
大小的内核:-(n-1), -(n-2),... -1, 0, +1,... +(n-1), +n,