在我以前的这篇文章中,曾经介绍过Mandelbrot集,并给出了c++的实现方法。当时的我编程水平有限,用了很多笨办法,最终的效果图也不是很美观。主要问题有两个:第一,我以前的着色方法是把每个坐标点的模值映射到一个RGB值,然后画出来。这样做带来了一个很大的问题,那就是分形图案的发散速度是非常快的,迭代一些次数之后,其模值会很快超过double的有效范围,所以导致画出来的图又难看又不准。于是经过改进,我把每个像素点的不超过规定模值的最大迭代次数作为其颜色的标签,这样一来,既加快了运算速度,图形也变的准确美观了。第二个问题是颜色分配,我以前的方法是手工分配,这样既麻烦又不好看。我试验了一些方法之后,发现用小质数作为随机因子再对255取余的方法是最简单有效的,具体可以参阅后面的代码。
先说传统意义上的Mandelbrot集:对于复平面上的每一个点c, 用以下方法进行迭代:
a = 0;
while( a的模 < 4.0 且 迭代次数 < 阈值) //4.0 这个值的来源可以看wiki上的说明
{
a = a * a;
a = a + c;
}
得到的图形就是Mandelbrot集
分形图案的最大特点就是自相似性,每一个局部都有整体的特征。
注意到传统Mandelbrot集的迭代规则是a先平方,然后把结果加上该点的坐标。如果把平方改成立方会是什么样子呢?也是说把while循环里的内容改成这样:
a = a * a * a;
a = a + c;
像三次函数一样,3次Mandelbrot集的分形图案多了一个分叉,看上去大致是这样的:
类似的,4次Mandelbrot集是这个样子:
五次、六次的mandelbrot集也是大致如此,此处只贴原始图,就不一一放大了
五次的:
六次的:
最后,是程序源代码,转载请注明出外,谢谢
环境:vs2010, x64, opencv2.4.3
complex.h
#pragma once #include<iostream> #include<cmath> #include <vector> using namespace std; const double PI = 3.1415926535897932384626433832795; class Complex { private: void show(); double real; double image; public: Complex(){real=0;image=0;} ~Complex(){} Complex(double a,double b){real=a;image=b;} double& getReal(){return real;} double& getImage(){return image;} void setReal(double real){this->real = real;} void setImage(double image){this->image = image;} double getModulus(){return sqrt(real*real+image*image);} double getAngle() { if (image == 0) { if (real >= 0) return 0; else return PI; } else return atan(real / image); } static std::vector<Complex> sqrtc(Complex a, int n=2); Complex operator +(Complex &other); Complex operator +(const double &other); Complex operator +(const int &other); Complex operator -(Complex &other); Complex operator -(const double &other); Complex operator -(const int &other); Complex operator *(Complex &other); Complex operator *(const double &other); Complex operator *(const int &other); Complex operator /(Complex &other); Complex operator /(const double &other); Complex operator /(const int &other); void operator +=(Complex &other); void operator +=(const double &other); void operator +=(const int &other); void operator -=(Complex &other); void operator -=(const double &other); void operator -=(const int &other); void operator *=(Complex &other); void operator *=(const double &other); void operator *=(const int &other); void operator /=(Complex &other); void operator /=(const double &other); void operator /=(const int &other); Complex operator =(Complex &other); Complex operator =(const double &other); Complex operator =(const int &other); bool operator ==(Complex &other); bool operator ==(const double &other); bool operator ==(const int &other); friend ostream& operator<<(ostream &os,Complex &other); friend istream& operator>>(istream &is,Complex &other); };
complex.cpp
#include "complex.h" void Complex::show() { if(real>0&&image<0) printf("%g%gi",real,image); else if(real>0&&image>0) printf("%g+%gi",real,image); else if(real<0&&image>0) printf("%g+%gi",real,image); else if(real<0&&image<0) printf("%g%gi",real,image); else if(real==0&&image!=0) printf("%gi",image); else if(real!=0&&image==0) printf("%g",real); else printf("0"); } Complex Complex::operator+(Complex &other) { Complex temp; temp.real=real+other.real; temp.image=image+other.image; return temp; } Complex Complex::operator +(const double &other) { Complex temp; temp.real=real+other; temp.image=image; return temp; } Complex Complex::operator +(const int &other) { Complex temp; temp.real=real+(double)other; temp.image=image; return temp; } Complex Complex::operator-(Complex &other) { Complex temp; temp.real=real-other.real; temp.image=image-other.image; return temp; } Complex Complex::operator -(const double &other) { Complex temp; temp.real=real-(double)other; temp.image=image; return temp; } Complex Complex::operator -(const int &other) { Complex temp; temp.real=real-(double)other; temp.image=image; return temp; } Complex Complex::operator*(Complex &other) { Complex temp; temp.real=(real*other.real-image*other.image); temp.image=(image*other.real+real*other.image); return temp; } Complex Complex::operator *(const double &other) { Complex temp; temp.real=real*other; temp.image=image*other; return temp; } Complex Complex::operator *(const int &other) { Complex temp; temp.real=real*(double)other; temp.image=image*(double)other; return temp; } Complex Complex::operator/(Complex &other) { Complex temp; temp.real=((real*other.real)+(image*other.image))/(other.real*other.real+other.image*other.image); temp.image=((image*other.real)-(real*other.image))/(other.real*other.real+other.image*other.image); return temp; } Complex Complex::operator /(const double &other) { Complex temp; temp.real=real/other; temp.image=image/other; return temp; } Complex Complex::operator /(const int &other) { Complex temp; temp.real=real/(double)other; temp.image=image/(double)other; return temp; } void Complex::operator+=(Complex &other) { this->real+=other.real; this->image+=other.image; } void Complex::operator +=(const double &other) { this->real+=other; } void Complex::operator +=(const int&other) { this->real+=(double)other; } void Complex::operator-=(Complex &other) { this->real-=other.real; this->image-=other.image; } void Complex::operator -=(const double &other) { this->real-=other; } void Complex::operator -=(const int &other) { this->real-=(double)other; } void Complex::operator*=(Complex &other) { this->real=(real*other.real-image*other.image); this->image=(image*other.real+real*other.image);; } void Complex::operator *=(const double &other) { this->real=real*other; this->image=image*other; } void Complex::operator *=(const int &other) { this->real=real*(double)other; this->image=image*(double)other; } void Complex::operator/=(Complex &other) { this->real=((real*other.real)+(image*other.image))/(other.real*other.real+other.image*other.image); this->image=((image*other.real)-(real*other.image))/(other.real*other.real+other.image*other.image); } void Complex::operator /=(const double &other) { this->real=real/other; this->image=image/other; } void Complex::operator /=(const int &other) { this->real=real/(double)other; this->image=image/(double)other; } Complex Complex::operator= (Complex &other) { this->real=other.real; this->image=other.image; return *this; } Complex Complex::operator =(const double &other) { this->real=other; this->image=image; return *this; } Complex Complex::operator =(const int &other) { this->real=(double)other; this->image=image; return *this; } bool Complex::operator ==(Complex &other) { if(this->real=other.real&&this->image==other.image) return true; else return false; } bool Complex::operator ==(const double &other) { if(this->real==other&&this->image==0) return true; else return false; } bool Complex::operator ==(const int &other) { if(this->real==(double)other&&this->image==0) return true; else return false; } ostream& operator<<(ostream &os,Complex &other) { other.show(); return cout; } istream& operator>>(istream &is,Complex &other) { is>>other.real; is>>other.image; return cin; } std::vector<Complex> Complex::sqrtc(Complex a, int n) { double r = a.getModulus(); double angle = a.getAngle(); vector<Complex> root(n); for (int i=0; i<n; i++) { double k = powf(r, 1.0/n); double b = cos((PI * 2.0 * i + angle) / n); double c = sin((PI * 2.0 * i + angle) / n); Complex z(k*b, k*c); root.at(i) = z; } return root; } /**//**//**//////////////////////////////END_TEMPLATE_CLASS_COMPLEX///////////////////////////////
fractal.h
#include "complex.h" #include "opencv2/opencv.hpp" enum FractalType{JUALIA, MANDELBROT}; struct Border { double xMin; double yMin; double xMax; double yMax; }; class Fractal { private: Complex offset; FractalType type; public: Fractal(){} Fractal(FractalType _type, Complex c = Complex(0,0)) : offset(c), type(_type){} cv::Mat generateFractalImage(Border border, CvScalar colortab[256] ); int Iteration(Complex a, Complex c); };
fractal.cpp
#include "Fractal.h" #include <Windows.h> int Fractal::Iteration(Complex a, Complex c) { double maxModulus = 4.0; int maxIter = 50; int iter = 0; double model = a.getModulus(); while ( iter < maxIter && model < maxModulus) { a = a * a * a ; a += c; iter++; model = a.getModulus(); } return iter; } cv::Mat Fractal::generateFractalImage(Border border, CvScalar colortab[256] ) { cv::Size size(500,500); double xScale = (border.xMax - border.xMin) / size.width; double yScale = (border.yMax - border.yMin) / size.height; cv::Mat img(size, CV_8UC3); for (int y=0; y<size.height; y++) { for (int x=0; x<size.width; x++) { double cx = border.xMin + x * xScale; double cy = border.yMin + y * yScale; Complex a(0.0, 0.0); Complex c(cx, cy); int nIter ; if (type == MANDELBROT) { nIter = Iteration(a, c); } else if (type == JUALIA) { nIter = Iteration(c, offset); } int colorIndex = (nIter) % 255; cv::Vec3b color; color.val[0] = colortab[colorIndex].val[0]; color.val[1] = colortab[colorIndex].val[1]; color.val[2] = colortab[colorIndex].val[2]; img.at<cv::Vec3b>(y,x) = color; } } return img; }
main.cpp
#include <iostream> #include "opencv2/opencv.hpp" #include "Fractal.h" #include <Windows.h> cv::Mat img; Fractal fractal(MANDELBROT); //mandelbrot set //图像大小 cv::Size imageSize(500,500); //坐标轴范围 Border border = {-2.0, -2.0, 2.0, 2.0}; //颜色 CvScalar colortab[256]; int randomColor[3] = {0,0,0}; //用来记录鼠标按下、弹起 cv::Point pt1(-1, -1); cv::Point pt2(-1, -1); //鼠标状态 enum _status{SELECTING, DONE}status = DONE; void drawSelectRect(cv::Mat img) { cv::Mat temp; img.copyTo(temp); cv::rectangle(temp, pt1, pt2, CV_RGB(0,255,0), 2); cv::imshow("img", temp); } void mouseEventCallback(int event, int x, int y, int flags, void* param) { if( event == CV_EVENT_LBUTTONDOWN) { if (x >= 0 && y >= 0 && x < imageSize.width && y < imageSize.height) { pt1 = cv::Point(x, y); status = SELECTING; } else { pt1 = cv::Point(0, 0); } } else if( event == CV_EVENT_LBUTTONUP) { pt2 = cv::Point(x,y); int dx = abs(pt2.x - pt1.x); int dy = abs(pt2.y - pt1.y); if (x >= 0 && y >= 0 && dx > 5 && dy > 5) { x = min(x, imageSize.width); y = min(y, imageSize.height); status = DONE; double DX = border.xMax - border.xMin; double DY = border.yMax - border.yMin; dx = min(dx, dy); //使pt1, pt2为正方形,等比显示 dy = min(dx, dy); pt2.x = pt1.x + dx; pt2.y = pt1.y + dy; double offX = DX / imageSize.width; double offY = DY / imageSize.height; if(pt1.x < pt2.x) { border.xMax = offX * pt2.x + border.xMin; border.xMin = offX * pt1.x + border.xMin; } else { border.xMax = offX * pt1.x + border.xMin; border.xMin = offX * pt2.x + border.xMin; } if(pt1.y < pt2.y) { border.yMax = offY * pt2.y + border.yMin; border.yMin = offY * pt1.y + border.yMin; } else { border.yMax = offY * pt1.y + border.yMin; border.yMin = offY * pt2.y + border.yMin; } img = fractal.generateFractalImage(border, colortab); imshow("img", img); } } else if(event == CV_EVENT_RBUTTONDOWN)//右键取消放大,重新选取放大区域 { } else if( event == CV_EVENT_MOUSEMOVE && status == SELECTING )//在选取过程中画矩形 { pt2 = cv::Point(x,y); double DX = border.xMax - border.xMin; //接下来转换放大的坐标 double DY = border.yMax - border.yMin; int dx = abs(pt1.x - pt2.x); int dy = abs(pt1.y - pt2.y); dx = min(dx, dy); //使pt1, pt2为正方形,等比显示 dy = min(dx, dy); pt2.x = pt1.x + dx; pt2.y = pt1.y + dy; if(pt1.x > 0 && pt1.y > 0 && pt2.x > 0 && pt2.y > 0 && abs(pt2.x - pt1.x) > 5 && abs(pt2.y - pt1.y) > 5) { drawSelectRect(img); } } } void initColor() { for (int i=0; i<256; i++) { colortab[i].val[0] = (i * 3 + randomColor[0]) % 255; //配色好看的要领是用小质数 colortab[i].val[1] = (i * 5 + randomColor[1]) % 255; colortab[i].val[2] = (i * 11 + randomColor[2]) % 255; } } void main() { initColor(); cv::namedWindow("img"); cv::setMouseCallback("img", mouseEventCallback, 0); img = fractal.generateFractalImage(border, colortab); imshow("img", img); int fileCount = 0; /*press "esc" to exit, press 's' to save the image to the current directory, press 'r' to reset the image, press 'c' to change the color */ std::cout<<"鼠标左键画框可以放大"<<std::endl; std::cout<<" 按 esc 退出"<<std::endl; std::cout<<" 按 s 保存当前图像到当前目录"<<std::endl; std::cout<<" 按 r 复位"<<std::endl; std::cout<<" 按 c 变换颜色"<<std::endl; while(1) { char key = cv::waitKey(1); if (key == 's') { char pBuf[1000]; GetCurrentDirectory(1000,pBuf); std::string path = pBuf ; path += "\\image"; char file[5]; itoa(fileCount, file, 10); path += file; path += ".bmp"; fileCount++; cv::imwrite(path, img); } if (key == 'r') { border.xMin = -2.0; border.yMin = -2.0; border.xMax = 2.0; border.yMax = 2.0; randomColor[0] = 0; randomColor[1] = 0; randomColor[2] = 0; initColor(); img = fractal.generateFractalImage(border, colortab); imshow("img", img); } if (key == 'c') { cv::RNG rng(GetTickCount()); randomColor[0] = rng.uniform(0,256); randomColor[1] = rng.uniform(0,256); randomColor[2] = rng.uniform(0,256); initColor(); img = fractal.generateFractalImage(border, colortab); imshow("img", img); } if (key == 27) { break; } } // system("pause"); }