![]() |
OpenCV 4.12.0
開源計算機視覺
|
上一個教程: 使用 XML / YAML / JSON 檔案進行檔案輸入和輸出
下一個教程: 使用通用內部函式向量化你的程式碼
| 相容性 | OpenCV >= 3.0 |
本教程的目標是演示如何使用 OpenCV 的 parallel_for_ 框架輕鬆地並行化你的程式碼。為了說明這個概念,我們將編寫一個程式來對影像執行卷積操作。完整的教程程式碼在此。
第一個前提條件是使用並行框架構建 OpenCV。在 OpenCV 4.5 中,按以下順序提供並行框架:
如你所見,OpenCV 庫中可以使用多種並行框架。有些並行庫是第三方庫,在構建前必須在 CMake 中顯式啟用,而另一些則隨平臺自動可用(例如 APPLE GCD)。
當多個執行緒同時嘗試寫入或讀寫特定記憶體位置時,就會發生競態條件。基於此,我們可以將演算法大致分為兩類:-
我們將以執行卷積為例,演示如何使用 parallel_for_ 並行化計算。這是一個不會導致競態條件的演算法示例。
卷積是一種廣泛應用於影像處理的簡單數學運算。在這裡,我們將一個較小的矩陣(稱為核)在影像上滑動,畫素值與核中對應值的乘積之和給出輸出中特定畫素的值(稱為核的錨點)。根據核中的值,我們得到不同的結果。在下面的示例中,我們使用 3x3 核(以其中心為錨點)對 5x5 矩陣進行卷積,生成 3x3 矩陣。可以透過用合適的值填充輸入來改變輸出的大小。 
有關不同核及其作用的更多資訊,請參閱此處
本教程將實現該函式的最簡單形式,它接受一個灰度影像(1 通道)和一個奇數邊長的方形核,並生成一個輸出影像。該操作不會就地執行。
parallel_for_ 函式,就地實現可能過於複雜。InputImage src, OutputImage dst, kernel(size n)
makeborder(src, n/2)
for each pixel (i, j) strictly inside borders, do:
{
value := 0
for k := -n/2 to n/2, do:
for l := -n/2 to n/2, do:
value += kernel[n/2 + k][n/2 + l]*src[i + k][j + l]
dst[i][j] := value
}
對於一個n 大小的核,我們將新增一個大小為 n/2 的邊界以處理邊緣情況。然後我們執行兩個迴圈,沿核移動並將乘積加到總和中
我們首先建立一個與源影像 (src) 大小相同的輸出矩陣 (dst),併為源影像新增邊界(以處理邊緣情況)。
然後,我們按順序遍歷源影像中的畫素,計算核和相鄰畫素值上的值。接著,我們將計算出的值填充到目標影像 (dst) 中的相應畫素。
在檢視順序實現時,我們可以注意到每個畫素都依賴於多個相鄰畫素,但一次只編輯一個畫素。因此,為了最佳化計算,我們可以將影像分成條帶,並透過利用現代處理器的多核架構,並行地對每個條帶執行卷積。OpenCV 的 cv::parallel_for_ 框架會自動決定如何高效地拆分計算,併為我們完成大部分工作。
我們首先宣告一個繼承自 cv::ParallelLoopBody 的自定義類,並重寫 virtual void operator ()(const cv::Range& range) const。
operator () 中的 range 代表將由單個執行緒處理的值子集。根據要求,可能存在不同的拆分 range 的方式,這反過來會改變計算。
例如,我們可以選擇
拆分影像的整個遍歷,並按以下方式獲取 [行, 列] 座標(如上所示程式碼)
然後我們將按以下方式呼叫 parallel_for_ 函式
拆分行並計算每一行
在這種情況下,我們使用不同的 range 呼叫 parallel_for_ 函式
要設定執行緒數,你可以使用:cv::setNumThreads。你還可以透過 cv::parallel_for_ 中的 nstripes 引數指定拆分數量。例如,如果你的處理器有 4 個執行緒,設定 cv::setNumThreads(2) 或設定 nstripes=2 應該會得到相同的結果,因為預設情況下它將使用所有可用的處理器執行緒,但只會將工作負載拆分到兩個執行緒上。
parallelConvolution 類並用 lambda 表示式替換來簡化並行實現兩種實現在以下情況下的執行時間:
This program shows how to use the OpenCV parallel_for_ function and compares the performance of the sequential and parallel implementations for a convolution operation Usage: ./a.out [image_path -- default lena.jpg] Sequential Implementation: 0.0953564s Parallel Implementation: 0.0246762s Parallel Implementation(Row Split): 0.0248722s
This program shows how to use the OpenCV parallel_for_ function and compares the performance of the sequential and parallel implementations for a convolution operation Usage: ./a.out [image_path -- default lena.jpg] Sequential Implementation: 0.0301325s Parallel Implementation: 0.0117053s Parallel Implementation(Row Split): 0.0117894s
並行實現的效能取決於你的 CPU 型別。例如,在 4 核 - 8 執行緒的 CPU 上,執行時間可能比順序實現快 6 到 7 倍。有很多因素可以解釋為什麼我們沒有達到 8 倍的加速:
在本教程中,我們使用了水平梯度濾波器(如上動畫所示),它會生成一個突出垂直邊緣的影像。