--- slug: 2024/11/playing-with-fire-log-density title: "Playing with fire: Log-density and color" date: 2024-11-15 14:00:00 authors: [bspeice] tags: [] --- So far, our `plot()` function has been fairly simple; map an input coordinate to a specific pixel, and color in that pixel. This works well for simple function systems (like Sierpinski's Gasket), but more complex systems (like our reference parameters) produce grainy images. In this post, we'll refine the image quality and add color to really make things shine. ## Image histograms :::note This post covers sections 4 and 5 of the Fractal Flame Algorithm paper ::: To start, it's worth demonstrating how much work is actually "wasted" when we treat pixels as a binary "on" (opaque) or "off" (transparent). We'll render the reference image again, but this time, track each time we encounter each pixel during the chaos game. When the chaos game finishes, find the pixel we encountered most frequently. Finally, "paint" the image by setting each pixel's transparency to ratio of times encountered divided by the maximum value: import CodeBlock from "@theme/CodeBlock"; import paintLinearSource from "!!raw-loader!./paintLinear" {paintLinearSource} import {SquareCanvas} from "../src/Canvas"; import FlameHistogram from "./FlameHistogram"; import {paintLinear} from "./paintLinear"; ## Log display While using a histogram to paint the image improves the quality, it also leads to some parts vanishing entirely. In the reference parameters, the outer circle is preserved, but the interior appears to be missing! To fix this, we'll introduce the second major innovation of the fractal flame algorithm: [tone mapping](https://en.wikipedia.org/wiki/Tone_mapping). This is a technique used in computer graphics to compensate for differences in how computers represent color, and how color is perceived by people. As a concrete example, high dynamic range (HDR) photography uses this technique to capture nice images of scenes with wide brightness ranges. To take a picture of something dark, you need a long exposure time. However, long exposures can lead to images that "wash out" and become pure white. By taking multiple pictures using different exposure times, we can combine them to create a final image where everything is visible. In fractal flames, this "tone map" is accomplished by scaling brightness according to the _logarithm_ of how many times we encounter a pixel. This way, "dark spots" (pixels the chaos game visits infrequently) will still be visible, and "bright spots" (pixels the chaos game visits frequently) won't wash out.
Log-scale vibrancy is also why fractal flames appear to be 3D... As explained in the Fractal Flame paper: > Where one branch of the fractal crosses another, one may appear to occlude the other > if their densities are different enough because the lesser density is inconsequential in sum. > For example, branches of densities 1000 and 100 might have brightnesses of 30 and 20. > Where they cross the density is 1100, whose brightness is 30.4, which is > hardly distinguishable from 30.
import paintLogarithmicSource from "!!raw-loader!./paintLogarithmic" {paintLogarithmicSource} import {paintLogarithmic} from './paintLogarithmic' ## Color Finally, we'll spice things up with the last innovation introduced by the fractal flame algorithm: color. By including a color coordinate in the chaos game, we can illustrate the transforms that are responsible for each part of an image. ### Palette Our first step is to define a color palette for the image. Fractal flames typically use a palette of 256 colors that transition smoothly from one to another. In the diagram below, each color in our palette is plotted on a small strip. Putting the strips side by side shows the palette for our image: import * as params from "../src/params" import {PaletteBar} from "./FlameColor" ### Color coordinate import paintColorSource from "!!raw-loader!./paintColor" {paintColorSource} import FlameColor from "./FlameColor";