OpenCV學習筆記(四十四)——初探GPU
好久沒有更新啦,感覺最近沒有什麼特別的收穫值得和大家分享,還是有些懶,TLD結束了也沒有寫個blog做總結。還是和大家分享一下OpenCV的一個大家很少接觸的模組吧——GPU。這個部分我接觸的也是很少,只是根據教程和大家簡單交流一下,如果有高手有使用心得,歡迎多多批評。
OpenCV的GPU模組只支援NVIDIA的顯示卡,原因是該部分是基於NVIDIA的CUDA和NVIDIA的NPP模組實現的。而該模組的好處在於使用GPU模組無需安裝CUDA工具,也無需學習GPU程式設計,因為不需要編寫GPU相關的程式碼。但如果你想重新編譯OpenCV的GPU模組的話,還是需要CUDA的toolkit。
由於GPU模組的發展,使大部分函式使用起來和之前在CPU下開發非常類似。首先,就是把GPU模組連結到你的工程中,幷包含必要的標頭檔案gpu.hpp。其次,就是GPU模組下的資料結構,原本在cv名字空間中的現在都在gpu
需要再說明的是,在GPU模組中,矩陣的名字為GpuMat,而不是之前的Mat,其他的函式名字和CPU模組中相同,不同的是,現在的引數輸入不再是Mat,而是GpuMat。
還有一個問題就是,對於2.0的GPU模組,多通道的函式支援的並不好,推薦使用GPU模組處理灰度的影象。有些情況下,使用GPU模組的執行速度還不及CPU模組下的效能,所以可以認為,GPU模組相對而言還不夠成熟,需要進一步優化。很重要的一個原因就是記憶體管理部分和資料轉換部分對於GPU模組而言消耗了大量的時間。
需要注意的是,在所有使用GPU模組的函式之前,最好需要呼叫函式gpu::getCudaEnabledDeviceCount
還有一點就是使用GPU模組,需要在用CMake編譯OpenCV時使其中的WITH_CUDA和WITH_TBB的巨集生效,為ON。
由於我對GPU部分的熟悉程度還不行,先拿來一段sample自帶的一段求矩陣轉置的程式來做例子,程式碼如下:
#include <iostream> #include "cvconfig.h" #include "opencv2/core/core.hpp" #include "opencv2/gpu/gpu.hpp" #include "opencv2/core/internal.hpp" // For TBB wrappers using namespace std; using namespace cv; using namespace cv::gpu; struct Worker { void operator()(int device_id) const; }; int main() { int num_devices = getCudaEnabledDeviceCount(); if (num_devices < 2) { std::cout << "Two or more GPUs are required\n"; return -1; } for (int i = 0; i < num_devices; ++i) { DeviceInfo dev_info(i); if (!dev_info.isCompatible()) { std::cout << "GPU module isn't built for GPU #" << i << " (" << dev_info.name() << ", CC " << dev_info.majorVersion() << dev_info.minorVersion() << "\n"; return -1; } } // Execute calculation in two threads using two GPUs int devices[] = {0, 1}; parallel_do(devices, devices + 2, Worker()); return 0; } void Worker::operator()(int device_id) const { setDevice(device_id); Mat src(1000, 1000, CV_32F); Mat dst; RNG rng(0); rng.fill(src, RNG::UNIFORM, 0, 1); // CPU works transpose(src, dst); // GPU works GpuMat d_src(src); GpuMat d_dst; transpose(d_src, d_dst); // Check results bool passed = norm(dst - Mat(d_dst), NORM_INF) < 1e-3; std::cout << "GPU #" << device_id << " (" << DeviceInfo().name() << "): " << (passed ? "passed" : "FAILED") << endl; // Deallocate data here, otherwise deallocation will be performed // after context is extracted from the stack d_src.release(); d_dst.release(); }
以上介紹的內容不但膚淺,而且顯得比較凌亂。希望高手看完後多多指正,跟我一樣不太明白的朋友僅供參考。