神奇的分形图像(二)-Fractal Image-Mandelbrot And Julia

如果有跑了前篇的代码的小伙伴会发现拖动进度条的时候挺卡的;

这里呢将代码进行优化

将原来自定义的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倍左右

猜你喜欢

转载自blog.csdn.net/u012198575/article/details/81363919
今日推荐