【翻譯:OpenCV-Python教程】性能測量和技術提升

⚠️這個系列是自己瞎翻的,文法很醜,主要靠意會,跳着跳着撿重要的部分翻,翻錯了不負責,就這樣哈。

⚠️基於3.4.3,Performance Measurement and Improvement Techniques,原文

目標 

在處理圖像的過程中,既然你每秒都要處理大量的數據計算,這就強制要求你的代碼不僅僅要提供正確的解決邏輯,也要使用運算速度更快的方式。所以這一節,你會學到:

脫離開OpenCV,Python也提供了一個在測量執行時間時非常有用的模組 time 。另一個模組 profile 幫忙拿到代碼的詳細報告,比如代碼中每個方法要花掉多少時間,一個方法被調用了多少次等等。但是如果你使用IPython,所有的這些特性都被整合成了一種對用戶非常友好的方式。我們會遇到一些重要的特性,想要知道更多,查看  額外資源 這個部分裏的鏈接。

用OpenCV來測量性能 

cv.getTickCount 方法返回一個特定的事件(比如開機的時刻)之後到你調用此方法的時刻之間的時間週期數。因此你在(自己的業務)方法執行前和(自己的業務)方法執行後調用這個方法,你就能得到之前執行(自己的業務)方法所花費的時間週期數。

cv.getTickFrequency 方法返回時間週期的頻率,或者叫做每秒時間週期的數量。所以要得到每秒執行的次數,你可以像下面這樣做。

e1 = cv.getTickCount()

# your code execution

e2 = cv.getTickCount()

time = (e2 - e1)/ cv.getTickFrequency()

我們將會用以下例子來論證,下面的示例用一個從5到49,奇數距離修正的種子,來做中值濾波。(不用管這個結果是啥樣的,那不是我們的目標):

img1 = cv.imread('messi5.jpg')
e1 = cv.getTickCount()
for i in xrange(5,49,2):
    img1 = cv.medianBlur(img1,i)
e2 = cv.getTickCount()
t = (e2 - e1)/cv.getTickFrequency()
print( t )
# Result I got is 0.521107655 seconds

提示

你可以用 time 模組來做同樣的事,用 time.time() 方法代替 cv.getTickCount ,然後拿到兩個時間的差值。

OpenCV裏的默認優化 

許多 OpenCV 方法用 SSE2, AVX 等等優化過。但OpenCV也包含了未優化的代碼(譯者注:這對使用者是無感的,調用的方法相同,根據硬件和設置效率不同。)。所以如果你的系統支持這些特性我們就應該利用這些特性(幾乎所有的現代處理器都支持他們)。當編譯時它默認就開啓了,因此如果它被開啓了的話,OpenCV會運行優化過的代碼,否則運行未優化過的。你可以使用 cv.useOptimized() 方法來檢查它是開啓了還是被關閉了,用 cv.setUseOptimized() 方法來開啓或者關閉它,讓我們看一個簡單示例。

# check if optimization is enabled
In [5]: cv.useOptimized()
Out[5]: True
In [6]: %timeit res = cv.medianBlur(img,49)
10 loops, best of 3: 34.9 ms per loop
# Disable it
In [7]: cv.setUseOptimized(False)
In [8]: cv.useOptimized()
Out[8]: False
In [9]: %timeit res = cv.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 還是 y = np.square(x) ?我們將用timeit在IPython 的腳本中找到答案。

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倍左右(譯者注:結果中的單位-> ns-納秒10的-9次方秒,us-微秒10的-6次方秒)。如果你還考慮到數組的創建,它可能要快到(Numpy的)接近100倍。很酷,對吧?*(Numpy 的開發團隊已經在着手解決這個小問題了)*

提示

Python標量操作比Numpy標量操作更快。 因此對於包含一個或兩個元素的操作,Python標量優於Numpy數組。 當陣列的大小稍大時,Numpy會佔據優勢。

我們將再嘗試一個例子。 這次,我們將針對同一圖像比較 cv.countNonZero() 和 np.count_nonzero() 的性能。

In [35]: %timeit z = cv.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使用視圖而不是副本時。

IPython更多的神奇命令 

還有其他一些神奇的命令來衡量性能,分析,線性分析,內存測量等等。它們都有很好的文檔記錄。因此,此處僅提供指向這些文檔的鏈接。 建議有興趣的讀者試一試。(譯者注:在本節末尾的連接中)

性能優化技巧 

有幾種技術和編碼方法可以利用Python和Numpy最大化的性能。 此處僅註明相關的內容,並提供重要資源的鏈接。 這裏主要應該注意的是,首先嚐試以簡單的方式實現算法。 一旦代碼有效工作,分析它,找到瓶頸並進行優化它。

  1. 盡一切可能,避免在Python中使用循環,特別是雙/三循環。因爲循環本身就很慢。
  2. 儘可能把算法/代碼向量化,因爲Numpy和OpenCV都爲向量操作做過優化。
  3. 利用緩存的相關性。
  4. 除非必須,否則絕不拷貝數組。嘗試用視圖代替,拷貝數組是個消耗極大的操作。

即使照所有這些技巧做了之後,你的代碼仍然很慢,或者使用大型循環是不可避免的,請使用其他庫比如 Cython 來加快速度。

額外資源 

Exercises


上篇:【翻譯:OpenCV-Python教程】針對圖像的算法操作

下篇:【翻譯:OpenCV-Python教程】改變色彩空間

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章