目標
在圖像處理中,如果你每秒鐘需要進行大量的運算,你的代碼除了提供正確的解決方案,還應(yīng)該盡快的完成。所以在本節(jié),您將:
·測量你的代碼的性能
·一些提高你代碼性能的小技巧
·這些函數(shù):cv2.getTickCount, cv2.getTickFrequency
Python提供了一個模塊 time 來測量執(zhí)行的時間。另一個模塊profile來得到代碼詳細信息,比如代碼中每個函數(shù)的執(zhí)行時間,函數(shù)被調(diào)用了多少次等。但是如果你使用IPython,所有這些特性都整合在了一個用戶很友好的方式,我們會看到一些重要的
用OpenCV來測量性能
cv2.getTickCount 函數(shù)返回從一個參考時間(比如機器開機的時間)開始到這個函數(shù)被調(diào)用的時間之間的時鐘循環(huán)數(shù)量。所以如果你在函數(shù)執(zhí)行前調(diào)用一次,函數(shù)執(zhí)行完調(diào)用一次,你就能得到函數(shù)執(zhí)行用掉的時鐘循環(huán)。
cv2.getTickFrequency函數(shù)返回時鐘頻率或者每秒鐘的時鐘循環(huán)數(shù)。所以要得到函數(shù)執(zhí)行了多少秒,你可以:
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()函數(shù),然后計算兩次的差
OpenCV的默認優(yōu)化
很多OpenCV函數(shù)對SSE2, AVX等做了優(yōu)化。當然也有未優(yōu)化的代碼。所以如果我們的系統(tǒng)支持這些特性,我們應(yīng)該利用他們(基本上現(xiàn)在的主流處理器都支持)。在編譯的時候是自動啟用的。所以如果啟用的話OpenCV執(zhí)行的是優(yōu)化的代碼,你可以用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
可以看到,優(yōu)化的中值濾波速度是沒有優(yōu)化的版本的兩倍。如果你檢查代碼,你會看到中值濾波是SIMD優(yōu)化的。所以你可以用這個來在你的代碼上啟用優(yōu)化
用IPython來測量性能
有時候你可能需要比較兩個類似的運算的性能,IPython提供了一個魔法指令%timeit來干這個,它運行代碼若干次來得到準確結(jié)果,很適合用來測量單行代碼。
比如,你想知道下面哪個運算更好:
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倍。如果你算上創(chuàng)建數(shù)組,那要快100倍了,酷吧(Numpy 開發(fā)人員正在解決這個)
注意:
Python標量運算時比Numpy標量運算要快的。所以對于包含1到兩個元素的運算,Python標量要比Numpy數(shù)組要快。Numpy在數(shù)組尺寸有點大的時候占優(yōu)勢。
我們來看更多的例子,這次,我們會比較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的函數(shù)比Numpy的快25倍。
注意:
一般來說,OpenCV函數(shù)比Numpy函數(shù)要快,所以對于相同的運算,推薦優(yōu)先使用OpenCV函數(shù)。但是,也有例外,特別是當Numpy操作views而不是復(fù)制的時候。
更多IPython魔法命令
有其他一些魔法指令可以測量性能,profiling,line profiling,內(nèi)存測量等。
性能優(yōu)化技術(shù)
有一些技術(shù)和代碼方法來利用Python和Numpy的最大性能。要注意的是,首先用簡單的方法先實現(xiàn),如果正常工作了,再分析測量找到瓶頸然后優(yōu)化。
1.盡量少用Python的循環(huán),特別是兩層或者三層循環(huán),這天生的就慢
2.把代碼,算法盡可能的矢量化,因為Numpy和OpenCV對于適量操作是優(yōu)化過的。
3.利用緩存一致性。
4.除非必要,否則別用數(shù)組的復(fù)制。多用數(shù)組的視圖。數(shù)組復(fù)制是個很費的操作。
即便做到了所有這些。你的代碼可能還是慢。比如大規(guī)模循環(huán)無法避免,試試用其他庫,比如Cython,可能能讓它快點。