【问题标题】:Speed up angle calculation for each x,y point in a matrix加快矩阵中每个 x,y 点的角度计算
【发布时间】:2025-11-23 10:40:01
【问题描述】:

我有一个 3-d Numpy 数组 flow 如下:

flow = np.random.uniform(low=-1.0, high=1.0, size=(720,1280,2))
# Suppose flow[0] are x-coordinates. flow[1] are y-coordinates.

需要计算每个 x,y 点的角度。以下是我的实现方式:

def calcAngle(a):
    assert(len(a) == 2)
    (x, y) = a
    # angle_deg = 0
    angle_deg = np.angle(x + y * 1j, deg=True)
    return angle_deg

fangle = np.apply_along_axis(calcAngle, axis=2, arr=flow) 
# The above statement takes 14.0389318466 to execute

每个点的角度计算需要14.0389318466 seconds 在我的 Macbook Pro 上执行。

有没有一种方法可以加快速度,可能是通过使用一些矩阵运算,而不是一次处理每个像素。

【问题讨论】:

    标签: python performance numpy scipy vectorization


    【解决方案1】:

    您可以使用numpy.arctan2() 以弧度获取角度,然后使用numpy.rad2deg() 转换为度数:

    fangle = np.rad2deg(np.arctan2(flow[:,:,1], flow[:,:,0]))
    

    在我的电脑上,这比 Divakar 的版本快一点:

    In [17]: %timeit np.angle(flow[...,0] + flow[...,1] * 1j, deg=True)
    10 loops, best of 3: 44.5 ms per loop
    
    In [18]: %timeit np.rad2deg(np.arctan2(flow[:,:,1], flow[:,:,0]))
    10 loops, best of 3: 35.4 ms per loop
    

    使用np.angle() 的更有效方法是创建flow 的复杂视图。如果flownp.float64 类型的数组,形状为(m, n, 2),那么flow.view(np.complex128)[:,:,0] 将是np.complex128 类型的数组,形状为(m, n)

    fangle = np.angle(flow.view(np.complex128)[:,:,0], deg=True)
    

    这似乎比使用arctan2 后跟rad2deg 快一点(但差异不远高于timeit 的测量噪声):

    In [47]: %timeit np.angle(flow.view(np.complex128)[:,:,0], deg=True)
    10 loops, best of 3: 35 ms per loop
    

    请注意,如果 flow 正在创建为某个其他数组的转置,或者使用大于 1 的步长创建为另一个数组的切片,这可能不起作用。

    【讨论】:

    • 不错!虽然这似乎不适用于复数,但 OP 的示例数据也不是。
    • 如果输入很复杂,OP的方法就行不通,所以我认为我们不必担心这种情况。
    • 我想这取决于你对“工作”的定义。 :) 我的回答应该是“数据是 。为什么流会有复杂的 x 和 y 分量?复杂的 x 分量和复杂的 y 分量之间的角度是什么意思?我的猜测是没有复杂输入的用例,所以我们不必担心这种可能性。”
    • 是的,正如 OP 提到的那样,这些是 x 和 y 坐标。所以,我想我们不必担心上下文:)
    【解决方案2】:

    numpy.angle 支持矢量化操作。因此,只需将第一列和第二列切片输入它以获取最终输出,就像这样 -

    fangle = np.angle(flow[...,0] + flow[...,1] * 1j, deg=True)
    

    验证 -

    In [9]: flow = np.random.uniform(low=-1.0, high=1.0, size=(720,1280,2))
    
    In [17]: out1 = np.apply_along_axis(calcAngle, axis=2, arr=flow)
    
    In [18]: out2 = np.angle(flow[...,0] + flow[...,1] * 1j, deg=True)
    
    In [19]: np.allclose(out1, out2)
    Out[19]: True
    

    运行时测试-

    In [10]: %timeit np.apply_along_axis(calcAngle, axis=2, arr=flow)
    1 loop, best of 3: 8.27 s per loop
    
    In [11]: %timeit np.angle(flow[...,0] + flow[...,1] * 1j, deg=True)
    10 loops, best of 3: 47.6 ms per loop
    
    In [12]: 8270/47.6
    Out[12]: 173.73949579831933
    

    173x+ 加速!

    【讨论】:

    • 谢谢!我以为我已经尝试过您指定的矩阵解决方案,但遇到了一些错误。也许我之前犯了一个错误..
    最近更新 更多