2012-07-07 87 views
17

現在我正在開發一些使用OpenCV API的應用程序(C++)。此應用程序處理視頻。OpenCV:C++和C性能比較

在個人電腦上,一切工作真的很快。今天我決定在Android上移植這個應用程序(使用相機作爲videoinput)。幸運的是,Android上有OpenCV,因此我只是將我的本機代碼添加到Android應用程序示例中。一切工作正常,除了性能。我對我的應用程序進行了基準測試,發現該應用程序以4-5 fps工作,實際上不可接受(我的設備具有singlecore 1ghz處理器) - 我希望它能以大約10 fps的速度工作。

它是否能夠完全重寫我的申請C?我知道使用諸如std::vector之類的東西對開發人員來說很舒服,但我並不在乎。

看起來OpenCV's C接口與C++接口具有相同的功能/方法。

我GOOGLE了這個問題,但沒有找到任何東西。

感謝您的任何建議。

+3

我對android並不熟悉,但使用OpenCV的C接口不會給你任何顯着的性能提升,因爲它仍然使用相同的基本代碼。 – Mohammad 2012-07-07 15:47:41

+0

我已經添加了一些更多的信息給我的答案,希望你會發現它有用。 – Sam 2012-07-07 19:45:50

回答

53

我在Android和優化方面做了很多工作(我寫了一個視頻處理應用程序,處理4ms的幀),所以我希望我會給你一些相關的答案。

OpenCV中的C和C++接口沒有太大的區別。有些代碼是用C編寫的,有一個C++包裝器,還有一些反之亦然。兩者之間的任何顯着差異(如Shervin Emami衡量)都是迴歸,錯誤修復或質量改進。你應該堅持最新的OpenCV版本。

爲什麼不重寫?

你會花很多時間,你可以用得更好。 C接口很麻煩,引入錯誤或內存泄漏的機會很高。在我看來,你應該避免它。

建議優化

A.開啓優化。

編譯器優化和缺少調試斷言都可以使您的運行時間發生重大變化。

B.配置您的應用。

首先在計算機上執行它,因爲它更容易。使用visual studio profiler來識別慢速部分。優化它們。永遠不要優化,因爲你覺得很慢,而是因爲你測量它。從最慢的功能開始,儘可能優化,然後慢一點。衡量你的改變,確保它確實更快。

C.專注於算法。

更快的算法可以提高數量級(100x)的性能。一個C++技巧會給你兩倍的性能提升。

經典技術:

  • 調整大小您的視頻幀要小一些。通常,您可以從200x300px圖像中提取信息,而不是1024x768。第一個地區的面積要小10倍。

  • 使用更簡單的操作,而不是複雜的操作。使用整數而不是浮點數。千萬不要在矩陣中使用double或者執行數千次的for循環。

  • 儘可能少的計算。您是否可以僅在圖像的特定區域跟蹤對象,而不是全部處理所有幀?您可以對非常小的圖像進行粗略/近似檢測,然後在全幀中以ROI對其進行優化?

D.使用C,其中它的事項

在循環中,它可能是有意義的用C的風格,而不是C++。指向數據矩陣或浮點數組的指針比mat.at或std :: vector <>快得多。通常瓶頸是嵌套循環。關注它。在所有地方替換矢量並且代碼化代碼是沒有意義的。

E.避免隱性成本

一些OpenCV函數轉換數據提高一倍,處理它,然後再轉換回輸入格式。小心他們,他們會殺死移動設備上的性能。示例:翹曲,縮放,類型轉換。此外,色彩空間轉換被認爲是懶惰的。希望直接從原生YUV獲取灰度。

F.使用矢量

ARM處理器實現量化與一個叫NEON技術。學會使用它。它是強大的!

一個小例子:

float* a, *b, *c; 
// init a and b to 1000001 elements 
for(int i=0;i<1000001;i++) 
    c[i] = a[i]*b[i]; 

可以如下重寫。它更詳細,但更快。

float* a, *b, *c; 
// init a and b to 1000001 elements 
float32x4_t _a, _b, _c; 
int i; 
for(i=0;i<1000001;i+=4) 
{ 
    a_ = vld1q_f32(&a[i]); // load 4 floats from a in a NEON register 
    b_ = vld1q_f32(&b[i]); 
    c_ = vmulq_f32(a_, b_); // perform 4 float multiplies in parrallel 
    vst1q_f32(&c[i], c_); // store the four results in c 
} 
// the vector size is not always multiple of 4 or 8 or 16. 
// Process the remaining elements 
for(;i<1000001;i++) 
    c[i] = a[i]*b[i]; 

較真say必須在彙編寫的,但對於一個普通的程序員,這是一個有點嚇人。我使用gcc intrinsics獲得了很好的結果,就像上面的例子。

另一種快速啓動的方法是在OpenCV中將手動編碼的SSE優化代碼轉化爲NEON。 SSE是Intel處理器中的NEON等價物,許多OpenCV功能都使用它,如here。這是uchar矩陣(常規圖像格式)的圖像過濾代碼。你不應該盲目地一個接一個地轉換指令,而應該以此爲例。

您可以在this blog以及後面的文章中閱讀更多關於NEON的內容。

G.注重形象捕獲

它可以是一個移動設備上慢得出奇。優化它是設備和操作系統特定的。

+0

您有關於如何輪詢相機的建議嗎?在Nexus 4上使用OpenCV 2.4.3.2的示例,註釋掉任何處理,我只在大多數分辨率下看到10fps,如果我下降到176x144,則只能看到20fps .... – 2013-02-09 02:51:38

+2

請勿使用OpenCV相機API。用Java API捕獲幀,並將它們傳遞給本機代碼。 – Sam 2013-02-09 06:21:21

+0

謝謝!我想我必須走這條路,調整一些東西,類似於iOS中的操作方式。你會認爲至少會有一個OpenCV例子適合吞吐量...... – 2013-02-09 20:47:06

4

shervin imami在他的網站上進行了一些性能測試。你可以檢查它來獲得一些想法。

http://www.shervinemami.info/timingTests.html

希望它能幫助。

(還有,如果你分享自己發現的地方,如果你得到任何方式的性能提升,這將是很好。)

+0

感謝您的回覆 - 我會看看那個測試,也許會創建自己的測試。 – ArtemStorozhuk 2012-07-07 15:49:48

6

在作出這樣的決定,你應該分析代碼來定位熱點你的代碼。沒有這些信息,你爲了加快速度而做出的任何改變都是猜測。你有沒有試過這個Android NDK profiler?

+0

感謝您的回覆 - 我會嘗試。 – ArtemStorozhuk 2012-07-07 15:55:04

+2

@Astor如果這不起作用,您可以隨時在調試器中反覆暫停應用程序,以瞭解其花費的大部分時間。 – 2012-07-07 15:56:38

3

我想這個問題需要公式化爲:C比C++更快嗎?答案是否定的。兩者都被編譯爲本地機器語言,而C++被設計成與C++一樣快速。至於STL(特別是ISO標準)也被設計並注意,它們的速度與指針+一樣快,因此它們提供了靈活性。 使用C的唯一原因是您的平臺不支持C++ 在我的謙虛態度中,不要將所有內容都轉換爲C,因爲您可能會獲得幾乎相同的性能。並嘗試改善你的代碼或使用opencv的其他功能來做你想做的事情。

不服氣?那麼編寫一個簡單的函數,一次用C語言編寫,一次用C++編寫,然後在1億次循環中運行它並自己測量時間。也許這有助於你做出正確的決定

3

我從來沒有在Android中使用C或C++。但在個人電腦中,您可以使C++的運行速度與C代碼一樣快(有時甚至更快)。大多數C++都是專門爲允許更多功能而設計的,但不以速度爲代價(模板在編譯時解決)。大多數編譯器都非常擅長優化你的代碼,而你的std :: vector調用將被內聯,代碼幾乎與使用本地C數組相同。

我建議你尋找另一種提高你的表現的方法。也許在Android中有一些多媒體硬件擴展可以訪問並用於優化代碼。

3

我注意到在多個測試是:

  1. C接口(IplImage結構)是多次訪問時的像素,而不是直接使用Mat.at(X,Y)的方法中,當我轉換更快我的C++應用程序爲C,我的blob檢測例程性能提高了3倍

  2. 當從外部應用程序(例如LabView)調用某些例程時,C++接口崩潰,而在C中調用相同例程時,它會工作。這是FindContours和cvFindContours

  3. C與嵌入式設備更兼容。不過,我還沒有做過這方面的工作。