在python中生成定向光流(HOOF)直方图的最有效方法

Posted

技术标签:

【中文标题】在python中生成定向光流(HOOF)直方图的最有效方法【英文标题】:Most efficient way to generate Histograms of Oriented Optical Flow (HOOF) in python 【发布时间】:2021-05-31 16:29:34 【问题描述】:

我有一个 498 帧长的图像序列,我使用 cv2.calcOpticalFlowFarneback 计算了光流。因此,现在我有 497 个向量图来表示我的运动向量,这些向量由大小和方向来描述。

我需要做的是生成一个直方图,其中在 x 轴上我有角度范围(以度为单位)。更具体地说,我有 12 个 bin,其中第一个 bin 包含方向为 0 < angle < 30 的所有向量,第二个 bin 包含方向为 30 < angle < 60 的所有向量,依此类推。相反,在 y 轴上,我需要得到每个 bin 中包含的那些向量的模的总和。

这里的问题是使用简单的for 循环和if 语句来完成所有这些需要很长时间:

#magnitude and angle are two np.array of shape (497, 506, 1378)

bins = [1,2,3,4,5,6,7,8,9,10,11,12]
sum = np.zeros_like(bins)

for idx in range(np.array(magnitude).shape[0]): # for each flow map, i.e. for each image pair
    for mag, ang in zip(magnitude[idx].reshape(-1), angle[idx].reshape(-1)): 
        if ang >= 0 and ang <= 30:
            sum[0] += mag
        elif ang > 30 and ang <= 60:
            sum[1] += mag
        elif ang > 60 and ang <= 90:
            sum[2] += mag
        elif ang > 90 and ang <= 120:
            sum[3] += mag
        elif ang > 120 and ang <= 150:
            sum[4] += mag
        elif ang > 150 and ang <= 180:
            sum[5] += mag
        elif ang > 180 and ang <= 210:
            sum[6] += mag
        elif ang > 210 and ang <= 240:
            sum[7] += mag
        elif ang > 240 and ang <= 270:
            sum[8] += mag
        elif ang > 270 and ang <= 300:
            sum[9] += mag
        elif ang > 300 and ang <= 330:
            sum[10] += mag
        elif ang > 330 and ang <= 360:
            sum[11] += mag

计算耗时约 3 小时。有人可以建议一种更好、更有效的方法来执行此计算吗?

提前致谢。


编辑

摆脱了条件句并使用 Numba 进一步加快了速度。以下代码计算时间不到 10 秒:

import numpy as np
from numba import jit

@jit(nopython=True) # Set "nopython" mode for best performance, equivalent to @njit
def hoof(magnitude, angle):
    sum = np.zeros(13)

    for idx in range(magnitude.shape[0]): # for each flow map, i.e. for each image pair
        for mag, ang in zip(magnitude[idx].reshape(-1), angle[idx].reshape(-1)): 
            sum[int((ang)//30)] += mag
    
    sum[11] += sum[12]

    return sum[0:12]

【问题讨论】:

【参考方案1】:

条件很慢。你应该尽可能避免它们。 Numpy 矢量化Numba JIT 也有助于大幅加快此类代码的速度。这是一个未经测试的示例:

import numba as nb

@nb.jit
def compute(magnitude, angle):
    s = np.zeros(12)
    for idx in range(magnitude.shape[0]):
        for mag, ang in zip(magnitude[idx].reshape(-1), angle[idx].reshape(-1)):
            if ang == 0:
                s[0] += mag
            elif ang > 0 and ang <= 360: # The condition can be removed if always true
                s[(ang-1)//30] += mag
    return s

# Assume both are Numpy array and angle is of type int.
# Note that the first call will be slower unless you precise the types.
compute(magnitude, angle)

【讨论】:

感谢您的回答。角度不是整数,但s[(ang-1)//30] += mag 确实更聪明。为什么要从mag 中减去 1?没有它不也可以吗? 如果你减去 1 并且你有像ang=0.3 这样的非常小的角度然后(ang-1)//30)= -1,这是错误的。我想这就是你假设角度是整数的原因。但是,如果没有-1,如果ang=360,您将遇到问题,因为(360//30)=12(超出范围)。无论如何,我使用了一种解决方法(参见上面的编辑),现在计算时间不到 10 秒 :) 谢谢! 我做了一个减法,因为 30、60 等被包括在内而不是被排除在外。确实,ang[30//30] == ang[1]。是的ang=0.3 不起作用,因为我期望该值是整数。您可以使用 floorceil 来代替浮点数(可能使用加法/减法,以便间隔像初始代码中一样有效)。

以上是关于在python中生成定向光流(HOOF)直方图的最有效方法的主要内容,如果未能解决你的问题,请参考以下文章

光流直方图是如何工作的? [关闭]

在 Plotly Python 中使用时间滑块绘制连续直方图

Hoof, Paper, Scissors(USACO)

在C中生成Segfault的最简单的标准符合方式是什么?

在python中生成密码

在 Python 中生成一个数字序列