標籤:地方 一半 模糊 建立 超執行緒 ble extend 提速 不能
影像處理的演算法複雜度通常都比較高,計算也相應比較耗時。利用CPU多執行緒能力可以大幅度加快計算速度。但是,為了保證多執行緒的結果和單線程處理的結果完全相同,映像的多線程計算有一些需要特別考慮的地方。
基本思路:為了能讓多個線程同時平行處理,那麼各自處理的資料不能有交集,這很好理解。那麼基本思路是將一副映像分成多個子塊,每個子塊資料肯定是沒有交集的,每個線程對一個子塊資料進行處理,完成後將所有子塊處理結果合成最終映像。
首先,每個子塊的大小當然是必須考慮的問題。通常當應用進行一個較長時間的操作,應該用合適的方式告知使用者。既然我們把映像分子塊處理,如果單個子塊處理時間很短,那麼每當有一個子塊的資料處理完成,我們就可以立即把它相應的處理結果展示給使用者。使用者就會看到這個映像各個部分的處理結果不斷展示出來,直至整個映像完成。這樣某種程度上用這種方式就是在告知使用者正在處理進行中,避免為了把整個影像處理完成,使用者需要等待太長時間。從這個角度來說,如果子塊尺寸取的太大,每個子塊計算時間肯定相應地加長,對於快速顯示部分處理結果給使用者是不利的。但是如果子塊太小,子塊總數就會增加,肯定會增加線程開銷和其他一些開銷(分割映像,分配子塊資料等等),對於總的計算時間是不利的。這是一個權衡問題,可以根據具體情況確定。
另外,很多影像處理都要考慮像素領域範圍的資訊,因此對於每個子塊的處理不能僅僅使用這個子塊的內容。具體地,對於靠近子塊邊緣的像素,還要把子塊外的部分像素資訊考慮進來,加入計算,才能保證相應像素的處理結果是正確的。準確來說,如果領域半徑為r(對方形或圓形領域來說,其他領域可做相應調整),那麼子塊處理所需要的所有資料是子塊四周向外擴充r像素的範圍。
CRect rect1, rect2;rect1.CopyRect(pRect[i]);rect1.InflateRect(extend, extend);rect2.CopyRect(pRect[i]);rect2.MoveToXY(extend, extend);if (rect1.top < 0){ rect2.OffsetRect(0, rect1.top); rect1.top = 0;}if (rect1.left < 0){ rect2.OffsetRect(rect1.left, 0); rect1.left = 0;}if (rect1.bottom > Height) rect1.bottom = Height;if (rect1.right > Width) rect1.right = Width;
代碼中extend就是子塊要向四周擴張的大小,其實就是領域半徑r。pRect[i]是分割的第i個子塊的大小。Height和Width是原圖的高寬,擴充子塊自然不能超過原圖的尺寸。那麼最後rect1就是計算所需要的資料在原圖中所在的領域範圍,應用原圖的尺寸對它進行了限制。由於我是把每個子塊當成一個新的映像進行處理,rect2就是新映像中子塊處理結果所在的位置,用它來合成最終映像。
最後關於線程具體建立銷毀,資源分派與回收,線程同步和通訊,不做具體討論。只討論一下在這裡多線程如何協調工作的問題。由於計運算元塊的線程只負責處理子塊,還需要有人來做分割子塊,分配資料給子塊計算線程等等工作。本來應該畫流程圖的,實在懶得畫了,這裡簡單描述一下幾個線程如何協調工作的,其實也很簡單。介面線程A,處理和使用者之間的互動,接受使用者命令,發送計算訊息給線程B。計算協調線程B接受A的訊息,分割子塊,分配子塊資料,建立子塊計算線程Ci。子塊計算線程Ci負責子塊計算,發送處理結果(成功或失敗)訊息給線程B或者A。介面線程A收到子塊完成訊息,可以立即顯示子塊處理結果,當然也可以什麼都不做,等到所有子塊處理完再顯示。協調線程B收到第i個子塊完成訊息,回收分配給線程Ci的資源,銷毀Ci。如果所有的Ci完成了工作,B發送影像處理完成的訊息給A,A可以接著做後續的工作。這裡單獨用了一個線程B來做子塊計算協調的工作,感覺這樣比較清晰一些。當然也可以讓介面線程A來做這個工作,協調的工作量也不是很大,這樣就可以不需要B線程。
單線程和多執行緒時間對比
多執行緒速度肯定不能簡單地是單線程處理速度的N倍,這隻是理想狀況。由於很多額外工作(線程開銷,準備每個線程的資料,處理結果的合成,線程間同步,映像子塊結合部的部分重複計算),多線程是不可能達到理想狀況的。下表列出了一副2400x1350大小的24bit映像分成了12個子塊,分別在一台I5 4300U(雙核四線程)筆記本上和一台I5 6500(四核四線程)台式機上,處理高斯模糊的大概的平均耗時。高斯模糊演算法是簡單的行列方向兩次一維計算,半徑取50。在我的測試中,還即時顯示了分塊的處理結果,速度上可能要更慢一點。
| |
亮度通道 |
RGB通道 |
| |
單線程 |
多線程 |
提速 |
單線程 |
多線程 |
提速 |
| I5 4300U(雙核四線程) |
1100毫秒 |
550毫秒 |
50% |
3060毫秒 |
1250毫秒 |
59% |
| I5 6500(四核四線程) |
670毫秒 |
240毫秒 |
64% |
1850毫秒 |
560毫秒 |
69.7% |
理想狀況下四線程耗時最多能減少75%,實際上肯定達不到。在雙核四線程平台上,對亮度通道處理,多線程比單線程耗時減少了一半(50%)。對RGB通道,多線程比單線程耗時減少了大概59%。在四核四線程平台上,多線程耗時在亮度通道和RGB通道處理時,分別減少了64%和69%。可以看到,多執行緒的加速效果還是相當明顯的。話說牙膏廠超執行緒的效果還是挺驚人的,不然在雙核CPU上耗時減少是不可能超過50%的。當然物理核心的數量就更重要了。
而且還可以看到一個現象,在單線程處理下,RGB三通道的處理耗時是亮度通道處理耗時3倍略少,約為2.8倍(亮度通道還包括一些在RGB和亮度之間轉換的額外計算量)。而在多線程下,RGB三通道的處理時間大大小於亮度一個通道處理時間的3倍,約為2.38倍。相對於單線程,節省的時間更多了。這是因為RGB的處理也是在子塊的一個線程中處理的,並沒有增加新的線程開銷。因此線程開銷也是必須考慮的一個因素,不可忽略。
影像處理的多線程計算