OpenCV-Python教程:9.性能测量和改进技术
目标
在图像处理中,如果你每秒钟需要进行大量的运算,你的代码除了提供正确的解决方案,还应该尽快的完成。所以在本节,您将:
·测量你的代码的性能
·一些提高你代码性能的小技巧
·这些函数:cv2.getTickCount, cv2.getTickFrequency
Python提供了一个模块 time 来测量执行的时间。另一个模块profile来得到代码详细信息,比如代码中每个函数的执行时间,函数被调用了多少次等。但是如果你使用IPython,所有这些特性都整合在了一个用户很友好的方式,我们会看到一些重要的
用OpenCV来测量性能
cv2.getTickCount 函数返回从一个参考时间(比如机器开机的时间)开始到这个函数被调用的时间之间的时钟循环数量。所以如果你在函数执行前调用一次,函数执行完调用一次,你就能得到函数执行用掉的时钟循环。
cv2.getTickFrequency函数返回时钟频率或者每秒钟的时钟循环数。所以要得到函数执行了多少秒,你可以:
e1 = cv2.getTickCount()
# your code execution
e2 = cv2.getTickCount()
time = (e2-e1)/cv2.getTickFrequency()
下面的例子:
img1 = cv2.imread('messi5.jpg')
e1 = cv2.getTickCount()
for i in xrange(5,49,2):
img1 = cv2.medianBlur(img1,i)
e2 = cv2.getTickCount()
t = (e2-e1)/cv2.getTickFrequency()
print t
# Result I got is 0.521107655 seconds
注意:
你也可以通过time模块来做这个,使用time.time()函数,然后计算两次的差
OpenCV的默认优化
很多OpenCV函数对SSE2, AVX等做了优化。当然也有未优化的代码。所以如果我们的系统支持这些特性,我们应该利用他们(基本上现在的主流处理器都支持)。在编译的时候是自动启用的。所以如果启用的话OpenCV执行的是优化的代码,你可以用cv2.useOptimized()来检查是否启用了,用cv2.setUseOptimized()来启用/禁用,看下面的例子
# check if optimization is enabled
In [5]: cv2.useOptimized()
Out[5]: True
In [6]: %timeit res = cv2.medianBlur(img,49)
10 loops, best of 3: 34.9 ms per loop
# Disable it
In [7]: cv2.setUseOptimized(False)
In [8]: cv2.useOptimized()
Out[8]: False
In [9]: %timeit res = cv2.medianBlur(img,49)
10 loops, best of 3: 64.1 ms per loop
可以看到,优化的中值滤波速度是没有优化的版本的两倍。如果你检查代码,你会看到中值滤波是SIMD优化的。所以你可以用这个来在你的代码上启用优化
用IPython来测量性能
有时候你可能需要比较两个类似的运算的性能,IPython提供了一个魔法指令%timeit来干这个,它运行代码若干次来得到准确结果,很适合用来测量单行代码。
比如,你想知道下面哪个运算更好:
x = 5; y = x ** 2,
x = 5; y = x * x,
x = np.uint8([5]); y = x * x
x = np.uint8([5]);y = np.square(x)
我们可以这么做:
In [10]: x = 5
In [11]: %timeit y=x**2
10000000 loops, best of 3: 73 ns per loop
In [12]: %timeit y=x*x
10000000 loops, best of 3: 58.3 ns per loop
In [15]: z = np.uint8([5])
In [17]: %timeit y=z*z
1000000 loops, best of 3: 1.25 us per loop
In [19]: %timeit y=np.square(z)
1000000 loops, best of 3: 1.16 us per loop
可以看到,x = 5; y = x*x 是最快的,比Numpy的要快20倍。如果你算上创建数组,那要快100倍了,酷吧(Numpy 开发人员正在解决这个)
注意:
Python标量运算时比Numpy标量运算要快的。所以对于包含1到两个元素的运算,Python标量要比Numpy数组要快。Numpy在数组尺寸有点大的时候占优势。
我们来看更多的例子,这次,我们会比较cv2.countNonZero()和np.count_nonzero()作用于同一张图片的性能。
In [35]: %timeit z = cv2.countNonZero(img)
100000 loops, best of 3: 15.8 us per loop
In [36]: %timeit z = np.count_nonzero(img)
1000 loops, best of 3: 370 us per loop
OpenCV的函数比Numpy的快25倍。
注意:
一般来说,OpenCV函数比Numpy函数要快,所以对于相同的运算,推荐优先使用OpenCV函数。但是,也有例外,特别是当Numpy操作views而不是复制的时候。
更多IPython魔法命令
有其他一些魔法指令可以测量性能,profiling,line profiling,内存测量等。
性能优化技术
有一些技术和代码方法来利用Python和Numpy的最大性能。要注意的是,首先用简单的方法先实现,如果正常工作了,再分析测量找到瓶颈然后优化。
1.尽量少用Python的循环,特别是两层或者三层循环,这天生的就慢
2.把代码,算法尽可能的矢量化,因为Numpy和OpenCV对于适量操作是优化过的。
3.利用缓存一致性。
4.除非必要,否则别用数组的复制。多用数组的视图。数组复制是个很费的操作。
即便做到了所有这些。你的代码可能还是慢。比如大规模循环无法避免,试试用其他库,比如Cython,可能能让它快点。