我正在撰寫一個軟體,它可以在很多(潛在的大)影像上進行大量影像操作/合成。
多執行緒有助于提高速度,但 QT 不允許在同一影像上同時使用多個 QPainter。
因此,我必須在副本上的每個執行緒中進行影像操作/合成,然后將其 blit,這會大大降低性能(顯然取決于用例)。
所以我想出了一個似乎可行但感覺非常hacky的想法。
我得到目標影像資料 ( QImage::bits) 指標,并將其提供給作業執行緒。
在作業執行緒中,我從提供的指標重新創建一個新的 QImage 。這意味著,沒有復制,沒有 blitting。只要我確保每個像素/圖塊僅在一個執行緒中作業并且我不分離目標影像,它似乎作業正常。
我的問題是:這安全嗎?這種方法是否會出現其他問題?
示例代碼
QImage source = ...;
QImage target = ...;
QPainter::CompositionMode compositionMode = QPainter::CompositionMode_SourceOver;
// calculate tiles
QList<QRect> tiles;
for(int y = rect.top(); y < rect.top() rect.height(); y = tileSize){
for(int x = rect.left(); x < rect.left() rect.width(); x = tileSize){
QRect tile(
x, y,
x tileSize > rect.left() rect.width() ? rect.left() rect.width() - x : tileSize,
y tileSize > rect.top() rect.height() ? rect.top() rect.height() - y : tileSize
);
tiles.append(tile);
}
}
// Get target pixel pointer and do threaded operation on each tile
uchar *targetPix = target.bits();
auto target_size = target.size();
auto targetFormat = target.format();
QList<int> lol = QtConcurrent::blockingMapped(tiles, [&target_size, &targetFormat, &source, targetPix, &compositionMode](const QRect &r){
QImage tile_target(targetPix, target_size.width(), target_size.height(), targetFormat);
QPainter p(&tile_target);
p.setCompositionMode(compositionMode);
// do you image operations here. For now we just do a simple draw
p.drawImage(r.topLeft(), source, r);
return 1; // In reallity this would return sensible data ;)
});
(這個例子在我的測驗中將速度提高了大約 4.6 倍。當然取決于作業系統和系統。)
uj5u.com熱心網友回復:
簡短的回答
這確實很棘手(但當您想要最先進的性能時通常需要這樣做),但它應該(可能使其)是執行緒安全的(對于某些操作)。當然,這取決于您在tile_target.
您甚至可以不訪問分配的圖塊之外的位(即tile_targetrect 之外的部分r),這取決于您。
一些注意事項
確保您只訪問分配的瓷磚的位
正如tile_target整個影像所指的那樣,您可以確保不訪問此目標圖塊之外的位。一些有問題的案例:
- 抗鋸齒:在執行此類操作時,您可能會更改相鄰的位
- 過濾:操作應該像模糊一樣經常讀取相鄰位以計算“平均”值。如果另一個執行緒可能同時寫入相同的位,則讀取也不安全。
可能的解決方案?:允許訪問和/或寫入圖塊的相鄰位的一種選擇是將影像劃分為條紋并分兩步處理影像:
- 首先同時處理偶數條
- 然后同時處理不均勻的條紋
此程序允許您修改一半的相鄰條紋(用于抗鋸齒)或(如果沒有人寫入)以訪問下一個和/或前一個條紋的所有位(用于過濾目的)。
這不應該顯著降低效率,如果您創建足夠的條帶以保持所有 cpu(s) 忙碌(即通常是 cpu(s) 支持的執行緒數的兩倍)。
我應該擔心分離嗎?
這不應該是您當前實施的問題。已經從任何其他可能存在的副本中QImage::bits分離(如果需要)影像 ( )。target當您通過阻塞呼叫執行緒來執行并發操作時。target至少只要影像存在,原始影像 ( ) 就會tile_target存在。
更安全的方法
使用專用于多執行緒影像處理的庫,或者至少允許參考子影像。
將 tile 的副本(請參閱 參考資料
QImage::copy)傳遞給每個執行緒并將結果寫回原始影像中(使用互斥體或通過在呼叫執行緒中執行此操作)。根據影像操作的計算不敏感性,這個額外的副本可能會或可能不會被忽略。對于 OP,這似乎不是一個可行的選擇。
請注意,在抗鋸齒或過濾的情況下,這些更安全的方法可能會與單執行緒結果產生(略微)不同的結果。這些偽影可以通過盡可能大的切片來最小化(即創建的切片不超過您的 CPU 支持的執行緒數)
使用 GPU
使用 GPU 時,影像處理通常要快得多,尤其是在過濾等方面。但這不是QImage開箱即用的支持。
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/509856.html
標籤:多线程qtqimage
