Numpy 沿轴查找图像的质心

Numpy 沿轴查找图像的质心,numpy,scipy,python-3.6,Numpy,Scipy,Python 3.6,我试图为图像的每一列找到一个重心。我目前使用的是scipy.ndimage.measurements.center_of the u 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) ret

我试图为图像的每一列找到一个重心。我目前使用的是
scipy.ndimage.measurements.center_of the u 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。沿\u轴应用\u
或迭代。

第二个版本稍微快一点,但使用矢量化可以使速度更快。(
沿_轴应用_
仅隐藏一个循环):

这段代码比必要的要复杂一些,因为它可以处理任意尺寸的图像和沿任意轴的重心。基本思想是使用
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):


无需计算两次-
com
似乎未使用。@kazemakase对不起,我的错,在代码中,我实际上没有计算两次。我现在将编辑问题。太糟糕了,这将是一个简单的优化:)输入的
img
看起来像什么(典型的形状和值范围)?它是一个形状~200x1800的2d数组。值的范围从0到255。我想出了一个稍微快一点的方法。我可以简单地使用质心函数的numpy实现:
np.round(np.sum(np.arange(m.shape[0])*m)/np.sum(m),1)
。虽然速度更快,但我想知道是否有一些numpy魔法矢量化版本:)我正要提出类似的建议。在您的numpy实现中,
m
是什么?一列还是整个图像?太好了,非常感谢!你介意解释一下代码是如何工作的吗?我已经用一些解释更新了答案。代码对我来说比文字更容易。。。希望它能有所帮助:)
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
img = np.random.randint(256, size=(200, 1800))
%timeit get_y_vec(img)