![]() |
OpenCV
开源计算机视觉
|
上一篇教程: 使用XML/YAML/JSON文件进行文件输入和输出
下一篇教程: 使用通用内联函数向量化你的代码
兼容性 | OpenCV >= 3.0 |
本教程的目标是演示如何使用OpenCV parallel_for_
框架轻松地并行化你的代码。为了说明这个概念,我们将编写一个程序来对图像执行卷积运算。完整的教程代码 在此。
第一个前提条件是OpenCV需要使用并行框架构建。在OpenCV 4.5中,按以下顺序提供以下并行框架:
如你所见,OpenCV库可以使用多种并行框架。一些并行库是第三方库,必须在构建之前在CMake中显式启用,而其他库则随平台自动提供(例如APPLE GCD)。
当多个线程尝试同时写入或读取和写入特定内存位置时,就会发生竞争条件。基于此,我们可以将算法大致分为两类:
我们将使用执行卷积的示例来演示如何使用parallel_for_
并行化计算。这是一个不会导致竞争条件的算法示例。
卷积是一个简单的数学运算,广泛用于图像处理。在这里,我们滑动一个较小的矩阵(称为内核)在图像上,像素值与内核中相应值的乘积之和给出输出中特定像素的值(称为内核的锚点)。根据内核中的值,我们得到不同的结果。在下面的示例中,我们使用一个3x3内核(其中心为锚点)并对5x5矩阵进行卷积以生成一个3x3矩阵。输出的大小可以通过用适当的值填充输入来改变。
有关不同内核及其功能的更多信息,请查看 此处
在本教程中,我们将实现该函数的最简单形式,该函数采用灰度图像(1通道)和奇数长度的方形内核并生成输出图像。该操作不会就地执行。
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),并向src图像添加边界(以处理边缘情况)。
然后,我们顺序迭代src图像中的像素,并计算内核和相邻像素值的value。然后,我们将value填充到dst图像中相应的像素。
观察顺序实现,我们可以注意到每个像素依赖于多个相邻像素,但每次只编辑一个像素。因此,为了优化计算,我们可以将图像分割成条带,并利用现代处理器的多核架构在每个条带上并行执行卷积。OpenCV 的 cv::parallel_for_ 框架会自动决定如何有效地分割计算,并为我们完成大部分工作。
我们首先声明一个继承自 cv::ParallelLoopBody 的自定义类,并重写 virtual void operator ()(const cv::Range& range) const
。
operator ()
中的范围表示将由单个线程处理的值的子集。根据需求,可能存在不同的范围分割方式,这反过来会改变计算。
例如,我们可以:
分割图像的整个遍历,并通过以下方式获取[行,列]坐标(如上代码所示)
然后,我们将以如下方式调用 parallel_for_ 函数:
分割行并为每一行计算
在这种情况下,我们使用不同的范围调用 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 倍的加速
在本教程中,我们使用了水平梯度滤波器(如上动画所示),它会生成突出显示垂直边缘的图像。