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

说在前面

并行执行的前提

  • 支持的框架
  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( ̄、 ̄)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值