博客專欄

        EEPW首頁 > 博客 > 獨家|OpenCV1.9 如何利用OpenCV的parallel_for_并行化代碼(附代碼)

        獨家|OpenCV1.9 如何利用OpenCV的parallel_for_并行化代碼(附代碼)

        發布人:數據派THU 時間:2021-08-20 來源:工程師 發布文章

        目標

        本教程的目標是展示如何使用OpenCV的parallel_for_框架輕松實現代碼并行化。為了說明這個概念,我們將編寫一個程序,利用幾乎所有的CPU負載來繪制Mandelbrot集合。完整的教程代碼可見原文。如果想了解更多關于多線程的信息,請參考本教程中提及的參考書或課程。

        預備條件

        首先是搭建OpenCV并行框架。在OpenCV3.2中,可以按此順序使用以下并行框架:

        1. 英特爾線程構建模塊(第三方庫,應該明確啟用)

        2. C =并行C / C ++編程語言擴展(第三方庫,應該明確啟用)

        3. OpenMP(集成的編譯器,應明確啟用)

        4. APPLE GCD(系統層面,自動使用(僅適用APPLE))

        5. Windows RT并發(系統層面,自動使用(僅適用Windows RT))

        6. Windows并發(部分運行時間,自動使用(僅適用Windows  -  MSVC ++> = 10))

        7. Pthreads (如果適用)

        正如前面所述,OpenCV庫可以使用多個并行框架。有些并行庫為第三方提供的庫,建立時應明確地用CMake(如TBB,C =)啟用,其余均為自動可用的平臺(例如APPLE GCD),但是,無論是直接使用并行框架還是利用CMake啟用并行框架并重建庫,首先要做的是啟用并行框架。

        第二個(弱)預備條件與任務相關,因為不是所有任務的計算都可以/適合以并行方式來運行。為了盡量保持簡單,可以將任務分解為與存儲器無關的多個元素,從而使其更加容易實現并行化。在計算機視覺處理過程中,由于大多數時間里一個像素的處理不依賴于其它像素的狀態,所以往往更加容易實現并行化。

        簡單的示例:繪制Mandelbrot集合

        這個例子中將展示如何繪制Mandelbrot集合,將普通的順序代碼實現并行化計算。

        理論

        Mandelbrot集合的名稱是數學家阿德里恩·多迪(Adrien Douady)為悼念數學家蒙德布羅特(Mandelbrot),以他的名字來命名的。它在數學界之外,作為分形類的一個例子,在圖像表示領域非常著名。Mandelbrot集合為一組自相似的重復圖案在不同尺度下重復顯示結果。為了進一步深入介紹,可以參考Wikipedia article。在這里,僅介紹利用公式繪制Mandelbrot集合(選自維基百科的文章)。

        Mandelbrot集合是在復平面中一組值C沿著0軌跡的二次迭代映射的邊界。

        1.png

        即,復數c作為Mandelbrot集的一部分,從 Z0 = 0開始重復進行迭代,當n趨近于無窮大時,Zn的絕對值的邊界值,它可以表示為:

        2.png

        偽代碼

        生成Mandelbrot集合的簡單的算法被稱為“逃逸時間算法”。為渲染圖像中的每個像素,根據復數值是否在邊界范圍之內,利用遞推關系進行測試。經過數次迭代之后,不屬于Mandelbrot集合的像素將快速逃逸,留下來的將是屬于Mandelbrot集合的像素。隨著計算時間的增加,迭代后的高階值將產生一個更詳細的圖像。在這里使用實現“逃逸”所需要的迭代次數來描繪圖像中的像素值。

        3.png

        將偽代碼和理論相關聯之后,得到:

        4.png

        在上圖中,復數的實部在x軸上,復數的虛部在y軸上。通過對圖形局部放大,可以看到整個形狀均重復可見。

        代碼實現

        逃逸時間算法的實現

        5.png

        在這里,我們使用了std::complex模板類來表示復數。利用這個函數來進行測試,以檢查像素是否在集合之中,并返回“逃逸”迭代。

        順序的Mandelbrot實現

        6.png

        在此程序中,通過依次遍歷渲染圖像中的像素來進行測試,以檢查像素是否屬于Mandelbrot集合。

        需要做的另一件事是把像素坐標轉換Mandelbrot集合空間:

        7.png

        最后,將灰度值分配給像素,使用以下規則:

        當迭代次數達到最大值時,像素為黑色(假定像素在Mandelbrot集合中);

        否則根據逃脫“逃逸迭代”和縮放尺度,為像素分配一個灰度值,以適應灰度范圍。

        8.png

        使用線性縮放轉換不足以感知的灰度變化。為了克服這個問題,使用一個平方根轉換來提升感知度(引用了Jeremy D. Frens博客中的內容): 9.png

        10.png

        綠色曲線對應于簡單的線性縮放轉換,藍色曲線對應于平方根轉換,可以從中觀察到的最低值如何沿著斜坡正向上升。

        并行Mandelbrot實現

        在順序的Mandelbrot實現中,每個像素被獨立計算。為了優化計算,我們可以利用現代處理器的多核架構并行執行多個像素的計算,利用OpenCV的CV :: parallel_for_框架可以輕松實現。

        11.png

        第一件事是聲明一個繼承CV :: ParallelLoopBody的自定義類,覆蓋virtual void operator ()(const cv::Range& range) const。

         operator ()表示將通過一個獨立的線程來處理像素的子集,這種拆分是自動完成的,以平均分配計算負荷,為此必須將像素索引坐標轉換成2D [行,列]坐標。還要注意的是,必須保持圖像的mat對象引用值,以便能夠適時地對圖像進行修改。

        調用并行執行程序:

        12.png

        在這里,range表示將要執行的操作總數,即圖像中的像素總數。使用CV :: setNumThreads設置線程數,還可以使用CV :: parallel_for_中的 nstripes參數指定拆分的數量CV :: parallel_for_。例如,如果處理器有4個線程,則設置CV :: setNumThreads(2)或者設置nstripes = 2應該是一樣的,默認情況下它會使用所有可用的處理器線程,但拆分后只有兩個線程。

        為了簡化并行的實現,C ++ 11標準刪除了ParallelMandelbrot類,采用lambda表達式代替它:

        13.png

        運行結果

        可以在原文找到完整的教程源代碼,并行實現的性能取決于CPU的種型。例如,在4核/ 8線程的CPU上,可以提速6.9倍左右。如果要問,為什么達不到8倍速,其中有很多因素;主要原因是由于:

        創建和管理線程的額外開銷;

        并行運行的后臺進程;

        帶2個邏輯線程的4硬件核與8硬件核之間是有區別的。

        由教程代碼生成的輸出圖像(可以對代碼進行修改,以使用更多次的迭代,根據逃逸迭代次數來分配像素顏色,并使用調色板以獲得更美的圖像):

        14.png

        Mandelbrot集合XMIN = -2.1,XMAX = 0.6,YMIN = -1.2,YMAX = 1.2,maxIterations = 500

        原文鏈接:

        https://docs.opencv.org/4.5.2/d7/dff/tutorial_how_to_use_OpenCV_parallel_for_.html

        *博客內容為網友個人發布,僅代表博主個人觀點,如有侵權請聯系工作人員刪除。



        關鍵詞: AI

        相關推薦

        技術專區

        關閉
        主站蜘蛛池模板: 锡林浩特市| 华阴市| 都匀市| 青田县| 临朐县| 台中市| 阳信县| 云龙县| 陕西省| 龙江县| 禹城市| 雅安市| 宁陕县| 天水市| 井陉县| 阿图什市| 马关县| 宜州市| 淄博市| 响水县| 平顺县| 哈巴河县| 岐山县| 东至县| 灵台县| 五河县| 贡觉县| 宁阳县| 白银市| 沛县| 大邑县| 廊坊市| 通许县| 泸定县| 东光县| 闻喜县| 扬州市| 敦化市| 武宣县| 垫江县| 靖西县|