在 C++ 中加速图像过滤器

Posted

技术标签:

【中文标题】在 C++ 中加速图像过滤器【英文标题】:Speed up image filter in C++ 【发布时间】:2014-06-28 21:11:38 【问题描述】:

我用 C++ 编写了一个图像过滤器。它采用具有 256 行的 32bpp 位图的每个像素(将像素逐行存储在连续数组中),对其在 5x5 区域内的相邻像素执行操作,并生成 4 个输出像素以使图像的分辨率为原始图像的 2 倍图片。这一切都在 CPU 上完成。

过滤器的运行速度没有我想要的那么快。我需要它始终运行在 16 毫秒以下。我可以用多线程加速它,还是开销不一致并且总共可能需要超过 16 毫秒?有没有其他方法可以加快速度?

如果多线程可以加快速度,那么实现越具体越好。任何代码示例将不胜感激!

【问题讨论】:

我认为多线程会很困难,因为像素都相互依赖以进行最终输出。我曾经做过类似的事情,我计算了如果某个像素的相邻值低于某个阈值,那么变化不会很明显,所以忽略它们,直到它附近的像素会发生更多变化。我不知道这是否适用于您的具体情况。 @Ellery, I think multi threading would be hard because the pixels all rely on each other for the final output,我相信,像素依赖于未修改的相邻像素。 我应该提到,输入像素永远不会被修改,每个输出像素都会被修改一次,但是由于每个输入像素都会读取其相邻像素,因此每个输入像素都会被读取多次。 为什么需要16ms以下的?如果是因为您的程序正在处理连续的帧流并且您希望达到 60 帧/秒,那么最好先确定您有多少个内核,然后将整个帧发送到每个内核,而不是细分每个帧在您可用的核心中。它也可能高度依赖于您的 CPU 缓存大小。 发布实现过滤器的处理代码的核心可能是个好主意,因为那里可能需要进行一些改进...... 【参考方案1】:

听起来您的问题是“微不足道的并行”,因此多线程绝对可以帮助实现近乎线性的改进。这是一个经典的数据细分问题。您可以将图像细分为 N 个区域( N = 您想要的线程数)。每个区域都需要图像的 (Dimension/N+4) 像素(每个区域将有几个重叠像素,“Ghost Cells”,以适应您正在应用的 5x5 内核)。然后所有线程写入公共“输出”区域的特定区域。由于线程不重叠其输出区域,因此您不需要任何互斥锁或锁定。

例如,如果您有一个 256x256 的图像并且您使用 4 个线程:

每个线程都有一条 (256 / 4 + 4) = 68 像素。它们自己独特的 64 像素区域和每边 2 个额外像素。他们在中心的 64 像素区域上进行计算(外部像素仅用于 5x5 卷积核) 或者,每个线程获得一个 132x132 块(每边 128x128 + 4 个),并且执行相同的操作。

这是一种经典的 MPI 细分技术,但在许多其他库中也很容易实现。

【讨论】:

我不确定它会起作用。例如,高斯模糊通常是内存有界的(如果用 SSE 编写),所以除非他仔细平铺图像处理,否则它不会产生加速。【参考方案2】:

手头的任务显然是高度可并行化的——这意味着多线程会有所帮助。总体思路是:

    为目标图像分配(连续)内存,考虑适当的间距/填充(如有必要) 算出你有多少 CPU 可用 将图像区域分成n 区域 - 行、列或矩形区域 产生 n 并行工作线程,每个线程负责一大块工作 如果您为这些线程设置关联掩码,这样它们每次处于活动状态时都会在同一个内核上执行,这肯定会有所帮助

【讨论】:

什么是关联掩码?将其保持在内存带宽限制以下怎么样? 亲和掩码指定为哪个威胁分配哪个 CPU 内核,换句话说,它保证威胁不会从 CPU 内核跳到 CPU 内核(因此称为“亲和性”)。内存带宽是你不能真正依赖的东西,因为它受主板物理架构/布局、内存控制器的性能、缓存特性等因素的影响——所有这些都超出了软件应用程序的访问范围(而且有充分的理由,也是)。

以上是关于在 C++ 中加速图像过滤器的主要内容,如果未能解决你的问题,请参考以下文章

C++ 图像处理 - 将图像文件读入二维数组

过滤加速度计数据噪声

Android加速度计过滤?

在循环中加速R过滤

使用选择器加速这个 jQuery 代码

C++ 内存不足:改为写入文件,在需要时读取数据?