如何利用OpenCV的parallel_for_并行化代碼
目标本教程的目标是展示如何使用OpenCV的parallel_for_框架輕松實現代碼并行化。為了說明這個概念,我們将編寫一個程序,利用幾乎所有的CPU負載來繪制Mandelbrot集合。完整的教程代碼在這裡。如果想了解更多關于多線程的信息,請參考本教程中提及的參考書或課程。
預備條件首先是搭建OpenCV并行框架。在OpenCV3.2中,可以按此順序使用以下并行框架:
正如前面所述,OpenCV庫可以使用多個并行框架。有些并行庫為第三方提供的庫,建立時應明确地用CMake(如TBB,C =)啟用,其餘均為自動可用的平台(例如APPLE GCD),但是,無論是直接使用并行框架還是利用CMake啟用并行框架并重建庫,首先要做的是啟用并行框架。
第二個(弱)預備條件與任務相關,因為不是所有任務的計算都可以/适合以并行方式來運行。為了盡量保持簡單,可以将任務分解為與存儲器無關的多個元素,從而使其更加容易實現并行化。在計算機視覺處理過程中,由于大多數時間裡一個像素的處理不依賴于其它像素的狀态,所以往往更加容易實現并行化。
簡單的示例:繪制Mandelbrot集合這個例子中将展示如何繪制Mandelbrot集合,将普通的順序代碼實現并行化計算。
理論Mandelbrot集合的名稱是數學家阿德裡恩·多迪(Adrien Douady)為悼念數學家蒙德布羅特(Mandelbrot),以他的名字來命名的。它在數學界之外,作為分形類的一個例子,在圖像表示領域非常著名。Mandelbrot集合為一組自相似的重複圖案在不同尺度下重複顯示結果。為了進一步深入介紹,可以參考Wikipedia article。在這裡,僅介紹利用公式繪制Mandelbrot集合(選自維基百科的文章)。
Mandelbrot集合是在複平面中一組值C沿着0軌迹的二次叠代映射的邊界。
即,複數c作為Mandelbrot集的一部分,從 Z0 = 0開始重複進行叠代,當n趨近于無窮大時,Zn的絕對值的邊界值,它可以表示為:
生成Mandelbrot集合的簡單的算法被稱為“逃逸時間算法”。為渲染圖像中的每個像素,根據複數值是否在邊界範圍之内,利用遞推關系進行測試。經過數次叠代之後,不屬于Mandelbrot集合的像素将快速逃逸,留下來的将是屬于Mandelbrot集合的像素。随着計算時間的增加,叠代後的高階值将産生一個更詳細的圖像。在這裡使用實現“逃逸”所需要的叠代次數來描繪圖像中的像素值。
将僞代碼和理論相關聯之後,得到:
在上圖中,複數的實部在x軸上,複數的虛部在y軸上。通過對圖形局部放大,可以看到整個形狀均重複可見。
代碼實現逃逸時間算法的實現
在這裡,我們使用了std::complex模闆類來表示複數。利用這個函數來進行測試,以檢查像素是否在集合之中,并返回“逃逸”叠代。
順序的Mandelbrot實現
在此程序中,通過依次遍曆渲染圖像中的像素來進行測試,以檢查像素是否屬于Mandelbrot集合。
需要做的另一件事是把像素坐标轉換Mandelbrot集合空間:
最後,将灰度值分配給像素,使用以下規則:
使用線性縮放轉換不足以感知的灰度變化。為了克服這個問題,使用一個平方根轉換來提升感知度(引用了Jeremy D. Frens博客中的内容):
綠色曲線對應于簡單的線性縮放轉換,藍色曲線對應于平方根轉換,可以從中觀察到的最低值如何沿着斜坡正向上升。
并行Mandelbrot實現在順序的Mandelbrot實現中,每個像素被獨立計算。為了優化計算,我們可以利用現代處理器的多核架構并行執行多個像素的計算,利用OpenCV的CV :: parallel_for_框架可以輕松實現。
第一件事是聲明一個繼承CV :: ParallelLoopBody的自定義類,覆蓋virtual void operator ()(const cv::Range& range) const。
operator ()表示将通過一個獨立的線程來處理像素的子集,這種拆分是自動完成的,以平均分配計算負荷,為此必須将像素索引坐标轉換成2D [行,列]坐标。還要注意的是,必須保持圖像的mat對象引用值,以便能夠适時地對圖像進行修改。
調用并行執行程序:
在這裡,range表示将要執行的操作總數,即圖像中的像素總數。使用CV :: setNumThreads設置線程數,還可以使用CV :: parallel_for_中的 nstripes參數指定拆分的數量CV :: parallel_for_。例如,如果處理器有4個線程,則設置CV :: setNumThreads(2)或者設置nstripes = 2應該是一樣的,默認情況下它會使用所有可用的處理器線程,但拆分後隻有兩個線程。
注
為了簡化并行的實現,C 11标準删除了ParallelMandelbrot類,采用lambda表達式代替它:
可以在這裡找到完整的教程源代碼,并行實現的性能取決于CPU的種型。例如,在4核/ 8線程的CPU上,可以提速6.9倍左右。如果要問,為什麼達不到8倍速,其中有很多因素;主要原因是由于:
由教程代碼生成的輸出圖像(可以對代碼進行修改,以使用更多次的叠代,根據逃逸叠代次數來分配像素顔色,并使用調色闆以獲得更美的圖像):
Mandelbrot集合XMIN = -2.1,XMAX = 0.6,YMIN = -1.2,YMAX = 1.2,maxIterations = 500
,更多精彩资讯请关注tft每日頭條,我们将持续为您更新最新资讯!