【opencv/core module】(九)How to use the OpenCV parallel_for_ to parallelize your code

本文介绍如何使用OpenCV并行计算Mandelbrot集合,通过对比串行和并行实现,展示了并行计算的优势。文章详细解释了Mandelbrot集合的概念,算法流程,并提供了串行和并行实现的代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

说在前面

并行执行的前提

  • 支持的框架
  1. Intel Threading Building Blocks (3rdparty library, should be explicitly enabled)
  2. C= Parallel C/C++ Programming Language Extension (3rdparty library, should be explicitly enabled)
  3. OpenMP (integrated to compiler, should be explicitly enabled)
  4. APPLE GCD (system wide, used automatically (APPLE only))
  5. Windows RT concurrency (system wide, used automatically (Windows RT only))
  6. Windows concurrency (part of runtime, used automatically (Windows only - MSVC++ >= 10))
  7. Pthreads (if available)
    咱们vs用的第6个?
  • 另一个前提,要并行化的任务应该尽量保证独立,即运行时没有相互联系(例如输入输出啥的)

测试用例-Mandelbrot set

  • Theory

    Mandelbrot set 是一个在复平面(复数)上的点集。其他参考:mandelbrot set

  • 算法流程

    在这里插入图片描述
    对于复平面上任意一点(x1,y1),即复数 z 0 = x 1 + i ⋅ y 1 ( i = − 1 ) z_0=x_1+i\cdot y_1 (i=\sqrt{-1}) z0=x1+iy1(i=1 ),若
    x 1 2 + y 1 2 > 4 x_1^2+y_1^2>4 x12+y12>4
    那么将该点的值置为0;否则,(a)
    z n = z n − 1 ∗ z n − 1 + z 0 z_{n} = z_{n-1}*z_{n-1} + z_0 zn=zn1zn1+z0
    再次计算,若
    z n . r e a l ( ) 2 + z n . i m a g ( ) 2 > 4 z_n.real()^2+z_n.imag()^2>4 zn.real()2+zn.imag()2>4
    将该点的值置为0;否则,跳转到(a)。
    由于上述的迭代可能会有无数次,所以我们需要设置一个最大迭代次数来终止迭代。

  • 伪代码

    我们采用最简单的“escape time algorithm”来生成Mandelbrot图像

    For each pixel (Px, Py) on the screen, do:
    {
    	x0 =scaled x coordinate of pixel(scaled to lie in the Mandelbrot X scale (-2, 1))
    	y0 =scaled y coordinate of pixel(scaled to lie in the Mandelbrot Y scale (-1, 1))
    	//x0,y0表示像素进行缩放后的坐标
    	//因为Mandelbrot图像显示的区域集中在x(-2,1),y(-1,1)
    	//而像素坐标往往上百上千,所以需要进行缩放,将像素坐标缩放至x(-2,1),y(-1,1)区域
    	x = 0.0
    	y = 0.0
    	iteration = 0//迭代次数
    	max_iteration = 1000//最大迭代次数
    	while (x*x + y*y < 2*2  AND  iteration < max_iteration) {
    		xtemp = x*x - y*y + x0//Mandelbrot 原理
    		y = 2*x*y + y0
    		x = xtemp
    		iteration = iteration + 1
    	}
    	color = palette[iteration]//把迭代次数作为颜色值
    	plot(Px, Py, color)
    }
    
  • 串行实现

    • 确定图像大小以及缩放比例
    Mat mandelbrotImg(4800, 5400, CV_8U);//高、宽
    float x1 = -2.1f, x2 = 0.6f;//确定区域,不是(-2,1)也没关系
    float y1 = -1.2f, y2 = 1.2f;
    float scaleX = mandelbrotImg.cols / (x2 - x1);//确定缩放比例
    float scaleY = mandelbrotImg.rows / (y2 - y1);
    
    • 实现 escape time algorithm
    int mandelbrot(const complex<float> &z0, const int max)
    {
    	complex<float> z = z0;
    	for (int t = 0; t < max; t++)
    	{
        	if (z.real()*z.real() + z.imag()*z.imag() > 4.0f) return t;
        	z = z*z + z0;
    	}
    	return max;
    }
    
    • 串行化代码
    int mandelbrotFormula(const complex<float> &z0, const int maxIter=500) {
        int value = mandelbrot(z0, maxIter);
        if(maxIter - value == 0)
        {
            return 0;
        }
    
        return cvRound(sqrt(value / (float) maxIter) * 255);
        //这里并没有直接return value;
        //而是进行了一个非线性的变换,目的是增加灰度图的区分度。
        //附 中有两者的对比效果图
    }
    
    void sequentialMandelbrot(Mat &img, const float x1,
    					 const float y1, const float scaleX, const float scaleY)
    {
        for (int i = 0; i < img.rows; i++)
        {
            for (int j = 0; j < img.cols; j++)
            {
                float x0 = j / scaleX + x1;//缩放
                float y0 = i / scaleY + y1;//缩放
    
                complex<float> z0(x0, y0);//复数
                uchar value = (uchar) mandelbrotFormula(z0);
                img.ptr<uchar>(i)[j] = value;//设置颜色值,灰度
            }
        }
    }
    
  • 并行实现

    • 定义一个继承自ParallelLoopBody类的自定义类并且重载操作符()
    class ParallelMandelbrot : public ParallelLoopBody
    {
    public:
        ParallelMandelbrot (Mat &img, const float x1, const float y1,
        				 const float scaleX, const float scaleY)
            : m_img(img), m_x1(x1), m_y1(y1), m_scaleX(scaleX), m_scaleY(scaleY)
        {
        }
    
        //Range中的每个像素都将会在单独的线程中进行计算
        //当然,总的线程数取决于你设置的最大线程数
        virtual void operator ()(const Range& range) const CV_OVERRIDE
        {
            for (int r = range.start; r < range.end; r++)
            {
                int i = r / m_img.cols;
                int j = r % m_img.cols;
    
                float x0 = j / m_scaleX + m_x1;
                float y0 = i / m_scaleY + m_y1;
    
                complex<float> z0(x0, y0);
                uchar value = (uchar) mandelbrotFormula(z0);
                m_img.ptr<uchar>(i)[j] = value;
            }
        }
    
        ParallelMandelbrot& operator=(const ParallelMandelbrot &) {
            return *this;
        };
    
    private:
        Mat &m_img;
        float m_x1;
        float m_y1;
        float m_scaleX;
        float m_scaleY;
    };
    
    • 调用
    ParallelMandelbrot parallelMandelbrot(mandelbrotImg, x1, y1, scaleX, scaleY);
    parallel_for_(Range(0, mandelbrotImg.rows*mandelbrotImg.cols), parallelMandelbrot);
    
    • 若喜欢用lambda表达式,可以直接使用下面的代码
    parallel_for_(Range(0, mandelbrotImg.rows*mandelbrotImg.cols), 
    										[&](const Range& range){
            for (int r = range.start; r < range.end; r++)
            {
                int i = r / mandelbrotImg.cols;
                int j = r % mandelbrotImg.cols;
    
                float x0 = j / scaleX + x1;
                float y0 = i / scaleY + y1;
    
                complex<float> z0(x0, y0);
                uchar value = (uchar) mandelbrotFormula(z0);
                mandelbrotImg.ptr<uchar>(i)[j] = value;
            }
        });
    

Result

  • 时间对比(上面并行、下面串行)
    在这里插入图片描述
    在这里插入图片描述

  • 使用线性值的结果图,少了很多细节
    在这里插入图片描述

END-(CSDN)2019.7.2( ̄、 ̄)

### 使用 OpenMP 并行编程优化 OpenCV 图像拼接算法 #### 1. 特征提取与描述阶段的并行化 在图像拼接过程中,特征提取和描述是最耗时的部分。对于多幅图像而言,每张图片都需要单独执行这一过程。由于这些操作相互独立,因此非常适合采用 OpenMP 来加速。 ```cpp #include <opencv2/opencv.hpp> #include <omp.h> void extractAndDescribeFeatures(const std::vector<cv::Mat>& images, std::vector<std::vector<cv::KeyPoint>>& keypoints, std::vector<cv::Mat>& descriptors) { int numImages = static_cast<int>(images.size()); #pragma omp parallel for schedule(static) for (int i = 0; i < numImages; ++i) { cv::Ptr<cv::FeatureDetector> detector = cv::ORB::create(); cv::Ptr<cv::DescriptorExtractor> extractor = cv::ORB::create(); detector->detect(images[i], keypoints[i]); extractor->compute(images[i], keypoints[i], descriptors[i]); } } ``` 这段代码展示了如何利用 `#pragma omp parallel for` 指令来指示编译器将循环体内的工作分配给多个线程同时运行[^2]。 #### 2. 特征匹配阶段的并行化 当完成所有图像的关键点检测后,下一步就是寻找最佳匹配对。同样地,在比较不同图像之间的特征向量时也可以引入并行机制: ```cpp std::vector<std::vector<cv::DMatch>> matches; cv::BFMatcher matcher(cv::NORM_HAMMING); // Assuming we have two sets of descriptors from previous step. for(size_t i=0;i<descriptors.size()-1;++i){ for(size_t j=i+1;j<descriptors.size();++j){ // Parallelize the matching process between pairs of descriptor lists #pragma omp task firstprivate(i,j) { std::vector<cv::DMatch> tempMatches; matcher.match(descriptors[i], descriptors[j], tempMatches); // Critical section to safely add results back into main container #pragma omp critical { matches.push_back(tempMatches); } } } } // Wait until all tasks are completed before proceeding further #pragma omp taskwait ``` 这里使用了 `#pragma omp task` 创建异步任务,并通过 `firstprivate` 关键字确保每个任务拥有自己的一份变量副本;而 `critical` 区块则用来保护共享资源访问的安全性。 #### 3. 变换估计与融合阶段 虽然这部分通常不是瓶颈所在,但如果涉及到大量数据处理的话仍然可以从并行计算中受益。比如,在构建全景图的过程中可以通过划分区域的方式让不同的处理器负责各自部分的像素映射和平滑过渡等工作。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值