我正在尝试为图像的每一列找到质心。我目前使用scipy.ndimage.measurements.center_of_mass
模块和简单的迭代来做到这一点。我目前的代码是:
def get_y_vec(img):
out = []
for col in img.T:
out.append(np.around(measurements.center_of_mass(col)[0], 1))
out = np.array(out)
return out
这样可以产生很好的效果,但是它很慢。是否有更高效的替代方案来实现相同的输出?
编辑:
我刚刚意识到质心是一个相当简单的数学,可以写成:
get_com = lambda m: np.round(np.sum(np.arange(m.shape[0])*m)/np.sum(m), 1)
def get_y_vec(img):
out = np.apply_along_axis(get_com, 0, img)
return out
虽然它快了4倍,但我的图像相当大,我觉得我们可以提出完全矢量化的解决方案,而不是使用np.apply_along_axis
或迭代。
答案 0 :(得分:1)
第二个版本略快,但使用矢量化可以更快。 (apply_along_axis
只是隐藏了一个循环):
def get_y_vec(img, axis=0):
n = img.shape[axis]
s = [1] * img.ndim
s[axis] = -1
i = np.arange(n).reshape(s)
return np.round(np.sum(img * i, axis=axis) / np.sum(img, axis=axis), 1)
代码有点复杂,因为它可以处理任何尺寸的图像和沿任意轴的重心。基本思想是创建一个带有arange
的索引向量,并对索引进行加权求和,并将图像值作为权重(与OP的第二个解决方案完全相同)。
一个更简单的版本,只能在列中运行,如下所示:
def get_y_vec(img):
n = img.shape[0]
i = np.arange(n).reshape([n, 1])
return np.round(np.sum(img * i, axis=0) / np.sum(img, axis=0), 1)
它利用numpy的两个矢量化特征:沿轴的广播和求和。首先,创建一个索引数组,其行数与图像中的行数相同,但只有一列。在img * i
中,矢量会自动与图像中的每一列相乘(这是广播)。然后,通过传递axis=0
,我们会指示sum
独立处理每个列,从而为每列产生一个值。
时间比较:
10 loops, best of 3: 71.8 ms per loop # scipy
10 loops, best of 3: 42.7 ms per loop # apply_along_axis
100 loops, best of 3: 1.67 ms per loop # vectorized
40倍的加速似乎并不太糟糕:)
基准代码(IPython):
img = np.random.randint(256, size=(200, 1800))
%timeit get_y_vec(img)