说在前面
- opencv版本:4.0.1
- 操作系统:win10
- vs版本:2017
- 官方文档:How to use the OpenCV parallel_for_ to parallelize your code
- 其他说明:自学,记录,demo
并行执行的前提
- 支持的框架
- Intel Threading Building Blocks (3rdparty library, should be explicitly enabled)
- C= Parallel C/C++ Programming Language Extension (3rdparty library, should be explicitly enabled)
- OpenMP (integrated to compiler, should be explicitly enabled)
- APPLE GCD (system wide, used automatically (APPLE only))
- Windows RT concurrency (system wide, used automatically (Windows RT only))
- Windows concurrency (part of runtime, used automatically (Windows only - MSVC++ >= 10))
- 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+i⋅y1(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=zn−1∗zn−1+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( ̄、 ̄)