![]() |
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 倍的加速:
在本教程中,我们使用了水平梯度滤波器(如上动画所示),它会生成一个突出垂直边缘的图像。