如果有跑了前篇的代码的小伙伴会发现拖动进度条的时候挺卡的;
这里呢将代码进行优化
将原来自定义的CMandelbrot 更改为 ParallelMandelbrot(继承自ParallelLoopBody类)
ParallelLoopBody类:
主要是为了重构virtual void operator()(const Range &range)const函数
在调用parallel_for_()函数时调用上述重构函数
而parallel_for_()便是这次代码优化的核心、利用多线程跑代码、提升效率。
代码如下:
#include <opencv2/opencv.hpp>
#include <opencv2/core.hpp>
#include <opencv2/imgcodecs.hpp>
#include <iostream>
#include <math.h>
using namespace cv;
using namespace std;
const int trackbar_val_max = 200;
/*分形方式*/
#define FRACTAL_Mandelbrot 1 //Mandelbrot
#define FRACTAL_Julia 2 //Julia
#define Mandelbrot_parallel
#ifndef Mandelbrot_parallel
#define Mandelbrot_sequential
#else
#endif
//#define TestImage
#ifndef TestImage //测试 Julia图 根据实部和虚部变换
#define TestTime //测试Mandelbrot_parallel 和 Mandelbrot_sequential的效率比
#else
#endif
int mandelbrot(const complex<float> &z0, const int max,const complex<float> &z1)
{
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 + z1;
}
return max;
}
int mandelbrotFormula(const complex<float> &z0,const complex<float> &z1, const int maxIter=255) {
int value = mandelbrot(z0, maxIter, z1);
if(maxIter - value == 0)//if value == maxIter, set pixel = 0
{
return 0;
}
return cvRound(sqrt(value / (float) maxIter) * 255);// in order to overcome linear scaling is make no sense to change of gray
}
class ParallelMandelbrot : public ParallelLoopBody
{
public:
ParallelMandelbrot(Mat &image,float x1,float y1,float ScaleX,float ScaleY, complex<float>& z, bool iscolor, int flag)
:m_img(image), m_x1(x1), m_y1(y1), m_scaleX(ScaleX), m_scaleY(ScaleY), z1(z), IsColor(iscolor), fractal_way(flag)
{
}
uchar GetRangeColor(int r, int *i, int *j)const
{
*i = r / m_img.cols;
*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;
if(fractal_way == FRACTAL_Mandelbrot)
value = (uchar) mandelbrotFormula(z0,z0);
else
value = (uchar) mandelbrotFormula(z0,z1);
return value;
}
virtual void operator()(const Range &range)const
{
if(!IsColor)
{
for(int r = range.start; r < range.end; r++)
{
int i = 0,j = 0;
uchar value = GetRangeColor(r,&i,&j);
m_img.ptr<uchar>(i)[j] = value;
}
}
else
{
for(int r = range.start; r < range.end; r++)
{
int i = 0,j = 0;
uchar value = GetRangeColor(r,&i,&j);
if(value > 150)
m_img.at<Vec3b>(i, j) = Vec3b(0, 0, value);
else if(value > 75)
m_img.at<Vec3b>(i, j) = Vec3b(0, value, saturate_cast<uchar>(value+150));
else if(value > 50)
m_img.at<Vec3b>(i, j) = Vec3b(0, value, 50);
else if(value > 10)
m_img.at<Vec3b>(i, j) = Vec3b(value+25, value, 50);
else if(value > 0)
m_img.at<Vec3b>(i, j) = Vec3b(value+25, 0, 50);
else
m_img.at<Vec3b>(i, j) = Vec3b(0, 0, 0);
}
}
}
ParallelMandelbrot &operator=(const ParallelMandelbrot&)
{
return *this;
}
Mat getImage(){return m_img;}
float getX1(){return m_x1;}
float getY1(){return m_y1;}
float getScaleX(){return m_scaleX;}
float getScaleY(){return m_scaleY;}
void SetRe_And_Im(float real,float imaginary){z1.real(real); z1.imag(imaginary);}
private:
bool IsColor;
int fractal_way;
complex<float> &z1;
Mat &m_img;
float m_x1;
float m_y1;
float m_scaleX;
float m_scaleY;
};
//逃逸时间算法实现
void sequentialManelbrot(Mat &img, const float x1, const float y1, const float scaleX, const float scaleY, complex<float> & c = complex<float>(0, 0) ,int flag = 0)
{
if(flag == 0)
{
if(img.channels() == 1)
{
for(int i = 0; i < img.rows; i++)
{
for(int j = 0; j < img.cols; j++)
{
float x0 = j / scaleX + x1;//img x point convert to Mandelbrot set
float y0 = i / scaleY + y1;//img y point convert to Mandelbrot set
complex<float> z0(x0,y0);
uchar value = (uchar)mandelbrotFormula(z0,z0);
img.ptr<uchar>(i)[j] = value;
}
}
}
else
{
for(int i = 0; i < img.rows; i++)
{
for(int j = 0; j < img.cols; j++)
{
float x0 = j / scaleX + x1;//img x point convert to Mandelbrot set
float y0 = i / scaleY + y1;//img y point convert to Mandelbrot set
complex<float> z0(x0,y0);
uchar value = (uchar)mandelbrotFormula(z0,z0);
if(value > 150)
img.at<Vec3b>(i, j) = Vec3b(0, 0, value);
else if(value > 75)
img.at<Vec3b>(i, j) = Vec3b(0, saturate_cast<uchar>(value + 75), saturate_cast<uchar>(value+150));
else if(value > 50)
img.at<Vec3b>(i, j) = Vec3b(0, value+50, 100);
else if(value > 10)
img.at<Vec3b>(i, j) = Vec3b(value+25, value+50, 100);
else if(value > 0)
img.at<Vec3b>(i, j) = Vec3b(value+25, 0, 100);
else
img.at<Vec3b>(i, j) = Vec3b(0, 0, 0);
}
}
}
}
else
{
if(img.channels() == 1)
{
for(int i = 0; i < img.rows; i++)
{
for(int j = 0; j < img.cols; j++)
{
float x0 = j / scaleX + x1;//img x point convert to Mandelbrot set
float y0 = i / scaleY + y1;//img y point convert to Mandelbrot set
complex<float> z0(x0,y0);
uchar value = (uchar)mandelbrotFormula(z0,c);
img.ptr<uchar>(i)[j] = value;
}
}
}
else
{
for(int i = 0; i < img.rows; i++)
{
for(int j = 0; j < img.cols; j++)
{
float x0 = j / scaleX + x1;//img x point convert to Mandelbrot set
float y0 = i / scaleY + y1;//img y point convert to Mandelbrot set
complex<float> z0(x0,y0);
uchar value = (uchar)mandelbrotFormula(z0,c);
if(value > 150)
img.at<Vec3b>(i, j) = Vec3b(0, 0, value);
else if(value > 75)
img.at<Vec3b>(i, j) = Vec3b(0, value, saturate_cast<uchar>(value+150));
else if(value > 50)
img.at<Vec3b>(i, j) = Vec3b(0, value, 50);
else if(value > 10)
img.at<Vec3b>(i, j) = Vec3b(value+25, value, 50);
else if(value > 0)
img.at<Vec3b>(i, j) = Vec3b(value+25, 0, 50);
else
img.at<Vec3b>(i, j) = Vec3b(0, 0, 0);
}
}
}
}
}
static float CalibrateRate(int pos)
{
float rate = pos * 1.0f / trackbar_val_max;
if(rate < 0.5)
rate = -2 * rate;
else
rate = 2 * rate;
return rate;
}
static void on_trackbar_Real(int pos, void * userdata)
{
int im_pos = getTrackbarPos("imaginary", "fractal");
ParallelMandelbrot *mm = (ParallelMandelbrot *)userdata;
float re_rate = CalibrateRate(pos);
float im_rate = CalibrateRate(im_pos);
#ifdef _DEBUG
cout << "re_rate" << re_rate << endl;
cout << "im_rate" << im_rate << endl;
#endif
#ifdef Mandelbrot_parallel
mm->SetRe_And_Im(re_rate,im_rate);
parallel_for_(Range(0, mm->getImage().rows * mm->getImage().cols), *mm);
#else
sequentialManelbrot(mm->getImage(),mm->getX1(),mm->getY1(),mm->getScaleX(),mm->getScaleY(),complex<float>(re_rate,im_rate),1);
#endif
imshow("fractal",mm->getImage());
}
static void on_trackbar_Imaginary(int pos , void * userdata)
{
int re_pos = getTrackbarPos("real", "fractal");
ParallelMandelbrot *mm = (ParallelMandelbrot *)userdata;
float re_rate = CalibrateRate(re_pos);
float im_rate = CalibrateRate(pos);
#ifdef _DEBUG
cout << "re_rate" << re_rate << endl;
cout << "im_rate" << im_rate << endl;
#endif
#ifdef Mandelbrot_parallel
mm->SetRe_And_Im(re_rate,im_rate);
parallel_for_(Range(0, mm->getImage().rows * mm->getImage().cols), *mm);
#else
sequentialManelbrot(mm->getImage(),mm->getX1(),mm->getY1(),mm->getScaleX(),mm->getScaleY(),complex<float>(re_rate,im_rate),1);
#endif
imshow("fractal",mm->getImage());
}
int main()
{
Mat mandelbrotImg(800, 600, CV_8UC3);
float x1 = -1.4f, x2 = 1.0f;
float y1 = -1.2f, y2 = 1.2f;
float scaleX = mandelbrotImg.cols / (x2 - x1);
float scaleY = mandelbrotImg.rows / (y2 - y1);
#ifdef TestImage
namedWindow("fractal",1);
ParallelMandelbrot mm(mandelbrotImg, x1, y1, scaleX, scaleY,complex<float>(0,0),true,FRACTAL_Julia);
int re = 40,im = 40;
createTrackbar("real","fractal",&re,trackbar_val_max,on_trackbar_Real,&mm);
createTrackbar("imaginary","fractal",&im,trackbar_val_max,on_trackbar_Imaginary,&mm);
waitKey();
#endif
//You can test the colorful image or gray image but you should change true to false and change to CV_8UC1
#ifdef TestTime
double t1 = (double) getTickCount();
#ifdef CV_CXX11
//! [mandelbrot-parallel-call-cxx11]
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,z0);
mandelbrotImg.ptr<uchar>(i)[j] = value;
}
});
//! [mandelbrot-parallel-call-cxx11]
#else
//! [mandelbrot-parallel-call]
//FRACTAL_Julia
//FRACTAL_Mandelbrot
ParallelMandelbrot parallelMandelbrot(mandelbrotImg, x1, y1, scaleX, scaleY,complex<float>(-0.4f,-0.65f),true,FRACTAL_Julia);
parallel_for_(Range(0, mandelbrotImg.rows*mandelbrotImg.cols), parallelMandelbrot);
//! [mandelbrot-parallel-call]
#endif
t1 = ((double) getTickCount() - t1) / getTickFrequency();
cout << "Parallel Mandelbrot: " << t1 << " s" << endl;
Mat mandelbrotImgSequential(800, 600, CV_8U);
double t2 = (double) getTickCount();
sequentialManelbrot(mandelbrotImgSequential, x1, y1, scaleX, scaleY,complex<float>(-0.4f,-0.65f),1);
t2 = ((double) getTickCount() - t2) / getTickFrequency();
cout << "Sequential Mandelbrot: " << t2 << " s" << endl;
cout << "Speed-up: " << t2/t1 << " X" << endl;
imwrite("Mandelbrot_parallel.png", mandelbrotImg);
imwrite("Mandelbrot_sequential.png", mandelbrotImgSequential);
system("pause");
#endif
}
实验结果:图就和上篇一样、但是运行效率而言,能获得较大改善
附上时间对比图: 效率提升了3倍左右