OpenCV GPU 簡單紀錄


OpenCV 實際上在 2011 年開始,就有一個「gpu」的模組,可以透過 NVIDIA CUDA 來使用顯示卡做 GPGPU 的計算了!目前官網上也有相關的文件了(連結)。

不過,現在 OpenCV 官網上,有提供已經建置好的 2.4.9 的程式可以下載,其中也包括了 Heresy 需要的 Windows 版;不過很遺憾的,官方所提供的預先建置好的版本,看來都沒有建置成支援 GPU 的版本…所以如果需要使用 OpenCV 的 GPU 功能的話,就需要自己下載原始碼來自己建置了。

而這篇,基本上就是 Heresy 自己試著用 Visual Studio 來建置 OpenCV 的 GPU 版本的紀錄。


下載 OpenCV 原始碼

而 OpenCV 的原始碼可以在 GitHub 上找到:

https://github.com/Itseez/opencv

不過首先要注意的是,他的「master」實際上是還在發展中的 3.0!不但裡面的模組(module)名稱做了改變,而且在使用介面上,也可能會有不同;更重要的是,這個版本應該還是有些問題在…所以如果要自行建置 OpenCV 2.x 的 GPU 版的話,需要切換到 2.4 的分支再下載:

https://github.com/Itseez/opencv/tree/2.4

如果是用 Git 來複製的話,則是記得複製後要切到 2.4 這個分支在建置,否則如果建置主線(3.0)的話,之後可能會有不少問題。

而如果之後會希望 OpenCV 可以方便更新的話,個人會建議用 Git 複製一份,以後要更新會相對方便。


用 CMake 產生專案

而由於 OpenCV 是用 CMake(官網)來作跨平台的處理,所以在下載 OpenCV 之後,會需要準備好 CMake 來產生 VisualStudio 的專案檔。而如果要下載的話,基本上是在下載頁面(連結),下載「cmake-3.0.0-win32-x86.zip」這個檔案就可以了。CMake 可以不用安裝,只要把 ZIP 檔解開後,執行 bin 下的「cmake-gui」就可以了。

在開啟 CMake 後,要先選擇原始碼的位置、也就是「Where is the source code」的選項、並且設定要把專案產生到哪裡(Where to build the binaries);而之後則是要點選「Configure」、並選擇要使用的開發環境。像以 Heresy 這邊,就是選「Visual Studio 11 2012 Win64」;不過這邊就是看每個人的需求了。

之後,CMake 就會開始分析、並輸出必要的檔案。在完成後,CMake 則會顯示出所偵測到的資訊、以及可以修改的參數,其畫面大致如下(Grouped 有勾選的話會有分類、比較好看):

如果是要建置支援 CUDA 的 OpenCV 的話,則是要確認「CUDA」分類下的參數是否有正確地偵測到;其中主要是「CUDA_TOOLKIT_ROOT_DIR」的路徑,他有正確地顯示、才代表他們有偵測到 NVIDIA CUDA SDK。如果沒有安裝 NVIDIA CUDA SDK 的話,也請先到 NVIDIA 官網下載、安裝後並重開機、再重新進行一次。

再來,則是要確認「WITH」下的「WITH_CUDA」有被勾選,這樣才代表有啟用 CUDA。而「WITH」和「ENABLE」裡面的其他項目,也可以看看,考慮視自己的需求來做調整;例如「WITH_OPENMP」之類的話,應該是可以考慮開啟的。

而如果想要縮短建置時間的話,則是可以到「BUILD」裡面,確認那些功能是自己需要的;如果有不需要的東西,則可以取消掉,這樣可以建置地快一點。

最後,如果希望建置出來的 OpenCV Binary 可以方便地轉移的話,會建議把「INSTALL」下的「INSTALL_CREATE_DISTRIB」把勾起來。

都完成後,則是要再按一次「Configure」,等到下面出現「Configuring done」,就算設定完成了,而上面本來紅底的部分,也都會變成白底的。不過這時候還不算完成,最後要再點選「Generate」,他才會產生出需要的專案檔;等到下面出現「Generating done」後,就算是完成輸出了~而這時候到輸出的資料夾下,就可以看到多了很多 Visual Studio 用的專案檔了~


建置專案

在使用 CMake 產生好專案後,接下來就是開啟建立出來的「OpenCV.sln」這個方案檔(solution)。在開啟方案後,接下來就是要找到「CMakeTargets」下的「INSTALL」這個專案,對他進行建置就可以了。

而他會去建置其他必要的專案,所以基本上只要建置了「INSTALL」,就會自動建立其他必要的專案;而由於 OpenCV 其實也算是一個相當大的函式庫,所以也會需要好一段時間才會建置完成(幾個小時),這時候也只能耐心等待了。

而要注意的是,根據需求的狀況,可能 DEBUG 和 RELEASE 都必須要建置一次,甚至 32 / 64 也都得分開處理…所以其實要建置一套完整的 OpenCV,是很花時間的。

建置好了之後,必要的檔案會被放在輸出目錄的「install」路徑下,以上圖的例子來說,就是「D:\Heresy\opencv2\install」。裡面的「include」資料夾,就是必要的 header 檔,而根據建置設定的部分,會出現其他的資校夾,裡面會放著 lib 和 dll 檔。

像是 Heresy 是使用 Visual Studio 2012(vc11)建置 x64 版,在底下也就會出現一個「x64」的資料夾、裡面則還會有一個「vc11」的資料夾;如果是其他的組態或開發環境的話,資料夾名稱也會有所不同。而在其下,「lib」資料夾就是 OpenCV 所有的 lib 檔,是在 linking 的時候要用的;「bin」的部分,則就是包含 dll 檔在內、執行階段要用的檔案了。


簡單的使用

如果已經熟系如何使用 OpenCV 的話,那要使用 OpenCV 來做簡單的 GPGPU 並不難。OpenCV 的 CUDA 支援全部做在「gpu」這個模組(官方文件)裡面,而東西則都在「cv::gpu」這個 namespace 下,使用的時候則是要 include「opencv2/gpu/gpu.hpp」這個 header 檔。

至於程式要怎麼寫呢?基本上,由於顯示卡的記憶體是獨立的,所以它的使用概念大致上還是三個步驟:

  1. 把資料從 CPU 複製到 GPU
  2. 在 GPU 上進行處理、計算
  3. 把結果從 GPU 複製回 CPU

而 OpenCV 在資料型別上,則是另外提供了 cv::gpu::GpuMat 這個類似 cv::Mat 的型別,用來管理 GPU 上的資料。如果要把一個 cv::Mat 物件的資料複製到 cv::gpu::GpuMat 也相當簡單,只要在建立 cv::gpu::GpuMat 物件的時候,把 cv::Mat 物件當參數給他就可以了。下面就是個簡單的例子:

cv::Mat mSource = cv::imread( argv[1] );
cv::gpu::GpuMat  mGSource( mSource );

或者,cv::gpu::GpuMat 也有提供一個 upload() 的函式,可以把 cv::Mat 的資料「上傳」到 GPU。

如果要把資料從 GPU 複製回來的話,基本上是使用 cv::gpu::GpuMatdownload() 函式,讓 OpenCV 把 GPU 的資料「下載」回來、寫到指定的 cv::Mat 物件裡面。下面就是個簡單的例子:

cv::Mat mTarget;
mGSource.download( mTarget );

而在影像處理的部分,基本上和本來城市的寫法是一樣的!只要從 namespace 從 cv 改成 cv::gpu 就可以了~OpenCV 基本上應該是已經把不少功能都實作 GPU 的版本了。

只要把要用 CUDA 來處理的影像,先從本來的  轉換成 ,然後再把影像處理的函式,也改用 cv::gpu 的版本就可以了!

下面就是一個把圖片透過 GPU 來縮小成 1/4(長寬各一半)的程式:

cv::Mat mSource = cv::imread( argv[1] );
cv::gpu::GpuMat  mGSource( mSource );

cv::gpu::GpuMat  mGTarget;
cv::gpu::resize( mGSource, mGTarget, cv::Size( 0, 0 ), 0.5, 0.5 );

cv::Mat mTarget;
mGTarget.download( mTarget );

cv::imwrite( std::string( argv[1] ) + ".cpu.tif", mTarget );

基本上大概就是這樣了。當然,OpenCV 在這一塊,也還有提供很多其他的進階功能,可能就需要參考官方的文件來研究了~

另外,「gpu」這個模組雖然名稱好像很通用,但是實際上它只是針對 NVIDIA 的顯示卡、使用 NVIDIA CUDA SDK 來作開發的;所以,如果電腦所安裝的是不支援 NVIDIA CUDA 的顯示卡的話,就無法使用了!

而如果是針對其他(AMD)顯示卡的話,或許可以考慮使用 OpenCV 的 OpenCL 模組(說明),使用上基本上和 gpu 模組的差異不大,只是 namespace 變成「cv::ocl」而已。不過,

以 Heresy 這邊使用 NVIDIA GeForce 卡做簡單的測試(使用 warpAffine() 這個函式),OpenCV 的 OpenCL 模組雖然比 CPU 快了許多,但是比 GPU 模組(CUDA)明顯慢了不少…不知道在 AMD 的平台上,有沒有可能會有更好的效能?或許等之後有時間,再來分享一些比較詳細的測試結果吧。


額外參考:

對「OpenCV GPU 簡單紀錄」的想法

  1. 呃…不好意思,想請教您一個問題~ 我從CMake開始按照您的方法做,最後也重新將VS該設置的重新設置了,包含屬性頁中的Include目錄、程式庫目錄與連結器中的輸入,將您的程式碼貼上Run了以後,停在cv::gpu::GpuMat mGSource( mSource );這一行,用逐步執行找,發現他停在dynamicuda.hpp裡的cudaSafeCall( cudaMallocPitch(devPtr, step, width, height) );(1134行)這一行,cmd畫面有出現可是就是不會繼續執行下去…OpenCV 2.4.11 + CUDA v6.5

    • 感覺這個問題有點像是沒辦法把資料複製到顯示卡上。

      個人會建議先試試看去執行 NVidia CUDA SDK 裡面的範例,確認你的 CUDA 環境能正確地運作;如果可以的話,再透過 OpenCV 提供的 API,確認是否可以正確抓到 CUDA 的裝置。

Elliot Huang 發表迴響 取消回覆

在下方填入你的資料或按右方圖示以社群網站登入:

WordPress.com 標誌

您的留言將使用 WordPress.com 帳號。 登出 /  變更 )

Google photo

您的留言將使用 Google 帳號。 登出 /  變更 )

Twitter picture

您的留言將使用 Twitter 帳號。 登出 /  變更 )

Facebook照片

您的留言將使用 Facebook 帳號。 登出 /  變更 )

連結到 %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.