如何使用 OpenCV parallel_for_并行化代码

根据内核中的值,我们得到不同的结果。因此,为了优化计算,我们可以利用现代处理器的多核架构,将图像拆分为条纹,并并行对每个条纹进行卷积。例如,如果您的处理器有 4 个线程,则设置或设置应与默认相同,它将使用所有可用的处理器线程,但仅在两个线程上拆分工作负载。在这里,我们将一个较小的矩阵(称为内核)滑动到图像上,像素值和内核中相应值的乘积之和为我们提供了输出中特定像素的值(称为。在本教程中,我们将实现最简单的函数形式,该函数采用灰度图像(1 个通道)和奇数长度的方形内核并生成输出图像。该操作不会就地执行。
摘要由CSDN通过智能技术生成

目标

本教程的目的是演示如何使用 OpenCV 框架轻松并行化代码。为了说明这个概念,我们将编写一个程序来对图像执行卷积运算。完整的教程代码在这里。parallel_for_

前提

并行框架

第一个前提条件是使用并行框架构建 OpenCV。在 OpenCV 4.5 中,以下并行框架按此顺序提供:

  • 英特尔线程构建模块(第三方库,应显式启用)
  • OpenMP(集成到编译器,应显式启用)
  • APPLE GCD(系统范围,自动使用(仅限 APPLE))
  • Windows RT 并发(系统范围,自动使用(仅限 Windows RT))
  • Windows 并发(运行时的一部分,自动使用(仅限 Windows - MSVC++ >= 10))
  • Pthreads(线程)

如您所见,OpenCV 库中可以使用多个并行框架。一些并行库是第三方库,必须在构建之前在 CMake 中显式启用,而其他并行库则在平台中自动可用(例如 APPLE GCD)。

竞争条件

当多个线程尝试写入同时读取和写入特定内存位置时,会发生争用条件。基于此,我们可以将算法大致分为两类:-

  1. 只有单个线程将数据写入特定内存位置的算法。
    • 例如,在卷积中,即使多个线程可能在特定时间从一个像素读取,也只有一个线程写入特定像素。
  2. 多个线程可以写入单个内存位置的算法。
    • 查找轮廓、特征等。此类算法可能要求每个线程同时向全局变量添加数据。例如,在检测特征时,每个线程会将其图像各自部分的特征添加到公共向量中,从而创建争用条件。

卷积

我们将使用执行卷积的示例来演示如何使用 并行化计算。这是一个不会导致竞争条件的算法示例。parallel_for_

理论

卷积是一种简单的数学运算,广泛用于图像处理。在这里,我们将一个较小的矩阵(称为内核)滑动到图像上,像素值和内核中相应值的乘积之和为我们提供了输出中特定像素的值(称为内核的锚点)。根据内核中的值,我们得到不同的结果。在下面的示例中,我们使用一个 3x3 内核(锚定在其中心)并在 5x5 矩阵上进行卷积以生成 3x3 矩阵。可以通过用合适的值填充输入来改变输出的大小。

卷积示例矩阵.gif

卷积动画

有关不同内核及其作用的更多信息,请查看此处

在本教程中,我们将实现最简单的函数形式,该函数采用灰度图像(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 边框来处理边缘情况。然后,我们运行两个循环来沿内核移动,并将乘积相加

实现

顺序实现

void conv_seq(Mat src, Mat &dst, Mat 内核)

{

​ int rows = src.rows, cols = src.cols;

dst = Mat(rows, cols, src.type());

​ 照顾边缘值

​ make border = kernel.rows / 2;

​ int sz = kernel.rows / 2;

copyMakeBorder(src, src, sz, sz, sz, sz, BORDER_REPLICATE);

​ for (int i = 0; i <行; i++)

​ {

  • 26
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是使用OpenCVSharp分块的图像上并行计算Canny亚像素级别的匹配的C#代码示例: ```csharp using System; using System.Collections.Generic; using System.Threading.Tasks; using OpenCvSharp; namespace ParallelCanny { class Program { static void Main(string[] args) { // Load the image Mat image = Cv2.ImRead("test.jpg", ImreadModes.Grayscale); // Split the image into blocks List<Mat> blocks = SplitImage(image, 8); // Create a parallel loop to process each block Parallel.For(0, blocks.Count, i => { Mat block = blocks[i]; // Apply Canny edge detection to the block with sub-pixel accuracy Mat canny = new Mat(); Cv2.Canny(block, canny, 100, 200, 3, true); // Match template using normalized cross-correlation Mat result = new Mat(); Cv2.MatchTemplate(block, canny, result, TemplateMatchModes.CCoeffNormed); // Find the max value and location double minVal, maxVal; Point minLoc, maxLoc; Cv2.MinMaxLoc(result, out minVal, out maxVal, out minLoc, out maxLoc); // Output the result Console.WriteLine("Block {0}: Max value = {1}, Max location = ({2}, {3})", i, maxVal, maxLoc.X, maxLoc.Y); }); } static List<Mat> SplitImage(Mat image, int numBlocks) { List<Mat> blocks = new List<Mat>(); int blockWidth = image.Width / numBlocks; int blockHeight = image.Height / numBlocks; for (int i = 0; i < numBlocks; i++) { for (int j = 0; j < numBlocks; j++) { Rect roi = new Rect(j * blockWidth, i * blockHeight, blockWidth, blockHeight); Mat block = new Mat(image, roi); blocks.Add(block); } } return blocks; } } } ``` 在此示例中,我们首先加载图像,然后将其分成8个块。接下来,我们使用并行循环处理每个块,对每个块应用Canny边缘检测,并使用归一的互相关匹配模板来查找亚像素级别的匹配。最后,我们输出每个块中最大值的位置和值。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值