沿轴线找到图像的质心

时间:2017-12-12 07:07:30

标签: numpy scipy python-3.6

我正在尝试为图像的每一列找到质心。我目前使用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或迭代。

1 个答案:

答案 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)