2017-04-26 86 views
1

我目前正试图从一篇论文在Python 2.7上使用OpenCV 3.1实现一个算法,但是这个过程花费的时间太长了。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循环',但这似乎是一种懒惰的方法,我只是绕过这个问题而不是修复它。

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

+0

它也取决于你如何构建OpenCV,所以你可以发布'getBuildInformation()'的输出。 –

+0

@MarkSetchell'cv2.getBuildInformation()'的输出太大而无法在评论中写入。你是否在想这个输出的具体内容? – Mira

回答

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

+0

感谢您的详细解答。我试着用numexpr快速实现,大约有4倍的加速。 – Mira

+0

@Mira真棒!在大多数情况下很难击败'cv2',所以很高兴看到'numexpr'给它一个很好的比赛并赢得比赛! – Divakar

+0

关于np.empty()的答案只是一个简短的问题。我真的不明白你会如何使用它。是不是np.zeros()初始化? 另外,尝试了np.zeros()方法,并且性能可能会有小的提升,但我们正在谈论的是以秒为单位测量程序的执行时间的差异。任何如何感谢你的建议;) – Mira