gpt4 book ai didi

multithreading - QT多线程QImage改变

转载 作者:行者123 更新时间:2023-12-05 04:23:16 27 4
gpt4 key购买 nike

我正在编写一个软件,它对很多(潜在的大)图像进行大量图像操作/合成。
多线程有助于提高速度,但 QT 不允许同时在同一图像上使用多个 QPainter。
所以我必须在副本的每个线程中进行图像操作/合成,然后将其 blit 回,这会大大降低性能(显然取决于用例)。

所以我想出了一个看似可行但感觉非常 hacky 的想法。
我得到目标图像数据 (QImage::bits) 指针,并将其提供给工作线程。
在工作线程中,我从提供的指针重新创建了一个新的 QImage。这意味着,没有复制,没有 blitting。它似乎工作正常,只要我确保每个像素/图 block 仅在一个线程中处理并且我不分离目标图像。

我的问题是:这种方法安全吗?这种方法是否会引发任何其他问题?

示例代码

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 倍的速度。当然取决于操作系统和系统。)

最佳答案

简答

这确实很棘手(但当您想要最先进的性能时通常需要这样做),但它应该(可能做到)线程安全(对于某些操作)。当然,这取决于您对 tile_target 执行的操作。

您甚至不能访问分配的图 block 之外的位(即 tile_target 的部分在 rect r 之外) ).

一些注意事项

确保您只访问指定磁贴的位

由于 tile_target 指的是整个图像,因此您需要确保不访问此目标图 block 之外的位。一些有问题的案例:

  • 抗锯齿:您可能会在执行此类操作时改变相邻部分
  • 过滤:操作应该像模糊一样经常读取相邻位以计算“平均”值。如果另一个线程可能同时写入相同的位,则读取也不安全。

可能的解决方案?:允许访问和/或写入图 block 相邻位的一种选择是将图像分成条纹并分两步处理图像:

  • 首先同时处理偶数条纹
  • 然后同时处理凹凸不平的条纹

此过程允许您修改相邻 strip 的一半(对抗锯齿有用)(如果没有人写入)以访问下一个和/或上一个 strip 的所有位(对过滤目的)。

这不会显着降低效率,如果您创建足够多的 strip 以保持所有 CPU 忙碌(即通常是您的 CPU 支持的线程数的两倍)。

我应该担心分离吗?

这不应该成为您当前实现的问题。 QImage::bits 已经从任何其他可能存在的副本中分离(如果需要)图像 (target)。当您通过阻塞调用线程执行并发操作时。至少只要 tile_target 图像存在,原始图像 (target) 就会存在。

更安全的方法

  • 使用专用于多线程图像处理的库,或者至少允许引用子图像。

  • 将图 block 的副本(参见 QImage::copy)传递给每个线程,并将结果写回原始图像(使用互斥锁或通过在调用中执行此操作线)。根据图像操作的计算不敏感度,这个额外的副本可能会也可能不会被忽略。对于 OP,这似乎不是一个可行的选择。

请注意,在抗锯齿或过滤的情况下,那些更安全的方法可能会生成与单线程结果(略有)不同的结果。这些伪影可以通过尽可能大的图 block 来最小化(即创建的图 block 不超过您的 cpu 支持的线程数)

使用GPU

使用 GPU 时,图像处理通常要快得多,尤其是对于过滤之类的事情。但这不是 QImage 开箱即用的支持。

关于multithreading - QT多线程QImage改变,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/73755281/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com