【问题标题】:OpenCV 3.1 optimizationOpenCV 3.1 优化
【发布时间】:2017-09-24 06:49:22
【问题描述】:

我目前正在尝试在 python 2.7 上使用 OpenCV 3.1 的论文实现算法,但该过程花费的时间太长。

给我带来麻烦的代码部分如下所示:

width, height = mr.shape[:2]
Pm = []
for i in d:
    M = np.float32([[1,0,-d[i]], [0,1,1]])
    mrd = cv2.warpAffine(mr, M, (height,width))
    C = cv2.subtract(ml, mrd)
    C = cv2.pow(C,2)
    C = np.divide(C, sigma_m)
    C = p0 + (1-p0)**(-C)
    Pm.append(C)

其中mlmrmrd 是 cv2 对象,dp0sigma_m 是整数。

最后 3 行中的除法和最终方程是这里真正的麻烦制造者。这个循环的每次迭代都是独立的,所以理论上我可以通过几个处理器拆分“for循环”,但这似乎是一种懒惰的方法,我只是绕过问题而不是修复它。

有人知道更快地执行这些计算的方法吗?

【问题讨论】:

  • 这还取决于您如何构建 OpenCV,以便您可以发布 getBuildInformation() 的输出。
  • @MarkSetchell cv2.getBuildInformation() 的输出太大,无法在评论中写入。您是否在考虑该输出中的任何具体内容?

标签: performance python-2.7 numpy opencv3.1


【解决方案1】:

我们可以利用numexpr module 将所有后面的算术运算作为一个评估表达式高效地执行。

因此,这些步骤:

C = cv2.subtract(ml, mrd)
C = cv2.pow(C,2)
C = np.divide(C, sigma_m)
C = p0 + (1-p0)**(-C)

可以用一个表达式代替 -

import numexpr as ne
C = ne.evaluate('p0 +(1-p0)**(-((ml-mrd)**2)/sigma_m)')

让我们验证一下。原来的方法是 func -

def original_app(ml, mrd, sigma_m, p0):
    C = cv2.subtract(ml, mrd)
    C = cv2.pow(C,2)
    C = np.divide(C, sigma_m)
    C = p0 + (1-p0)**(-C)
    return C

验证 -

In [28]: # Setup inputs
    ...: S = 1024 # Size parameter
    ...: ml = np.random.randint(0,255,(S,S))/255.0
    ...: mrd = np.random.randint(0,255,(S,S))/255.0
    ...: sigma_m = 0.45
    ...: p0 = 0.56
    ...: 

In [29]: out1 = original_app(ml, mrd, sigma_m, p0)

In [30]: out2 = ne.evaluate('p0 +(1-p0)**(-((ml-mrd)**2)/sigma_m)')

In [31]: np.allclose(out1, out2)
Out[31]: True

各种规模数据集的时间安排 -

In [19]: # Setup inputs
    ...: S = 1024 # Size parameter
    ...: ml = np.random.randint(0,255,(S,S))/255.0
    ...: mrd = np.random.randint(0,255,(S,S))/255.0
    ...: sigma_m = 0.45
    ...: p0 = 0.56
    ...: 

In [20]: %timeit original_app(ml, mrd, sigma_m, p0)
10 loops, best of 3: 67.1 ms per loop

In [21]: %timeit ne.evaluate('p0 +(1-p0)**(-((ml-mrd)**2)/sigma_m)')
100 loops, best of 3: 12.9 ms per loop

In [22]: # Setup inputs
    ...: S = 512 # Size parameter

In [23]: %timeit original_app(ml, mrd, sigma_m, p0)
100 loops, best of 3: 15.3 ms per loop

In [24]: %timeit ne.evaluate('p0 +(1-p0)**(-((ml-mrd)**2)/sigma_m)')
100 loops, best of 3: 3.39 ms per loop

In [25]: # Setup inputs
    ...: S = 256 # Size parameter

In [26]: %timeit original_app(ml, mrd, sigma_m, p0)
100 loops, best of 3: 3.65 ms per loop

In [27]: %timeit ne.evaluate('p0 +(1-p0)**(-((ml-mrd)**2)/sigma_m)')
1000 loops, best of 3: 878 µs per loop

大约 5x 可以在各种大小上加速,对于更大的阵列有更好的加速!

另外,作为旁注,我建议使用初始化数组,而不是像您在最后一步所做的那样追加。因此,我们可以在进入循环之前使用 out = np.zeros((len(d), width, height)) / np.empty 进行初始化,并在最后一步使用 out[iteration_ID] = C 分配到输出数组中。

【讨论】:

  • 感谢您的详细解答。我尝试使用 numexpr 进行快速实现,速度提高了大约 4 倍。
  • @Mira 太棒了!在大多数情况下很难击败 cv2,所以很高兴看到 numexpr 给它一个很好的竞争并获胜!
  • 只是一个关于您对 np.empty() 的回答的快速问题。我真的不明白你会如何使用它。是不是 np.zeros() 初始化?此外,尝试了 np.zeros() 方法,性能可能会略有提升,但是当程序的执行以秒为单位时,我们谈论的是厘秒的差异。无论如何感谢您的建议;)
  • @Mira 是的,如果你成功地使用了np.zeros((len(d), width, height)),那么我们可以使用np.empty 而不是np.zeros 具有相同的形状参数,但它的增益将是微不足道的算术运算完成了繁重的工作,我们在这里对其进行了改进。试试看,继续玩,反正我们都是这样学习的:)
  • 我决定尝试np.empty,令我惊讶的是,差异实际上非常明显。该程序似乎平均快了 200 毫秒。再次感谢!
猜你喜欢
  • 1970-01-01
  • 2020-05-18
  • 1970-01-01
  • 2014-07-07
  • 2012-09-29
  • 1970-01-01
  • 1970-01-01
  • 2014-03-22
  • 2017-04-07
相关资源
最近更新 更多