Background
The Buddhabrot is a fractal derived from the Mandelbrot set, which is also a fractal. If you'd like to first learn what the Buddhabrot is, you can do so here or here.
Ordinarily, a Buddhabrot is generated by iterating a large number of randomly chosen complex points according to the Mandelbrot equation and only considering orbit lengths between 1 and 100, for example. This method would yield an image like this.
97,000,000 orbits of lengths between 1 and 100. Highlights boosted in Photoshop. 
Consequently, in the same amount of computational time, fewer orbits are found, and as a result, the Buddhabrot is less dominated by random noise (above) and individual orbits can clearly be seen (below).
268 orbits of lengths between 100,000 and 250,000. 3rd root taken (explained later). 
When plotting the Buddhabrot conventionally, no distinction is made between individual orbits. They are all summed together to form the final Buddhabrot image.
In this method, each orbit is assigned a random RGB color and then added to the final Buddhabrot image. Here is some output using this method:
0.6388 + 0.3730i

0.3710  0.5960i

0.3485  0.6056i


178393

185217

157494

Each initial point is iterated normally, according to the Mandelbrot equation, and a temporary Buddhabrot matrix is created for just that one orbit. Here's what this matrix looks like for a single ordinary orbit.
Orbit of the point 0.3485  0.6056i. Orbit length = 157,494. (detail) 
For most orbits, the range of the image matrix  the difference between the value of the mosthit pixel and the leasthit pixel  is between 10 and 100. In the image above, for example, the range is 23.
However, there are orbits with extremely high ranges on the order of 750 or greater. These are problematic because when working with grayscale images, the RGB standard only allows for 256 shades of gray. Therefore it is technically impossible to view such orbits as they truly are. We can view an approximation of the orbit by rescaling the matrix to fit the RGB standard by multiplying the entire matrix by 255/max, where max is the highest value in the matrix.
Here is a single orbit with a range of about 1,000. This image was rescaled as described above.
Orbit of the point 0.2279 + 0.7457i. Orbit length = 128,435. Range before rescaling = 798. (detail) 
It is usually true that when Buddhabrot matrices have extremely high ranges, the frequency of each value between 0 and max appearing in the matrix is not uniform over the entire range. In other words, a handful of pixels are so exceptionally bright that they reduce darker regions to pure blacks when the image is rescaled.
To make an analogy, if you look up at the sky on a clear night, you will be able to see some number of bright stars. Yet there are also countless nebulae and dimmer stars you cannot see because the bright stars are so bright that they obscure everything else. In this case, trying to theoretically divide the perceived brightness by some number would be of no use in seeing the fainter objects. To do this, the proportionality between the light and dark regions must itself be altered.
The solution is taking the nth root of the entire matrix. This is a fairly standard data transformation. Consider the shape of the function y=sqrt(x), plotted here against y=x for comparison.
This function alters values more intelligently than simply dividing or multiplying them by a constant. The higher a value, the more it is downscaled. Meanwhile, lower values remain about the same (relatively speaking).
It is important to realize that this transformation reduces the range of the image. For example, taking the 3rd root reduces the range more than would taking the 2nd root. In my code, I determine which root, n, is necessary to reduce the range to a preset value, rangeSet. Here's the math behind this:
\[
\text{range}^{1/n} = \text{rangeSet}
\]
\[
1/n = \text{log}_{\text{range}} \text{rangeSet}
\]
\[
1/n = \frac{\text{log(rangeSet)}}{\text{log(range)}}
\]
\[
n = \frac{\text{log(range)}}{\text{log(rangeSet)}}
\]
Setting rangeSet equal to 255 would be logical. However, the more the range is reduced, the more of the orbit becomes visible. The tradeoff, though, is that there are fewer shades of gray in the resulting image. A good value for rangeSet, then, should expose as much of the orbit as possible while preserving enough shades of gray that the image appears smooth. I have found that a value of 25 works well. Here's the orbit shown earlier, before and after the transformation.Orbit of the point 0.2279 + 0.7457i before and after root transformation. (detail) 
Gallery
Here are some of the images I have generated with the techniques described above.
A few colorized orbits of lengths between 100,000 and 250,000. Highlights boosted in Photoshop. (detail) 
Approximately 250 orbits of lengths on the order of 100,000. Highlights boosted in Photoshop. (crop) 
Approximately 250 orbits of lengths on the order of 50,000. Saturated and highlights boosted in Photoshop. (crop) 
Normally, adding in colors early in the rendering process leads to a problem (as you described). That can be solved with histogram equalization and colorization added after that. You found a decent alternate solution which lets you keep the individually colored orbits. These are some very lovely results, Daniel.
ReplyDelete