Fractals/kf-extras
< Fractals
kf-extras: c programs by Claude Heiland-Allen for
- manipulating output from Kalles Fraktaler 2
- the exponential map (aka log polar or mercator projection) convertor
install
edit- git clone https://code.mathr.co.uk/kf-extras.git
- make
dependencies
editOpenEXR[1]
sudo apt install libopenexr-dev
Programs
editExemap: convert sequence of flat zoom images to the final location into an exponential map (unwrapped into log-polar coordinates)
- one long image
- side- by -side tiling
expmap n < x.txt > x.pgm # exponential map (log polar) with pseudo-de colour x.txt lists kfb files one per line, in order n is number of kfb files listed in x.txt
histogram d < x.kfb > x.pgm # histogram equalize (sort and search, O(N log N)) , d is density >= 1.0, affects the brightness curve
kfbtommit < x.kfb > x.mmit # convert to Mandel Machine iteration data format
pseudo-de < x.kfb > x.pgm # pseudo distance estimate colouring
rainbow < x.kfb > x.ppm # rainbow based on slope direction, plus pseudo-de
resize w h < x.kfb > x.kfb # resize to new (w,h) using bi-cubic interpolation
stretch < x.kfb > x.pgm # simple iteration count stretching
Scripts included ('bash' shell and common linux utilities required):
kfbtoogv.sh s work tmp
automatic movie maker with sound generation. Here
- s is desired movie length in seconds
- work is absolute path to dir containing
- *.kfb; tmp is absolute path to dir for output
it checks for the less-common programs it needs
exr
editkfb-to-exr test.kfb test.exr exrheader test.exr // utility to print an OpenEXR image file's header pnmcat -tb *.exr > *.ppm // file test.exr
Check header of exr file:
file fraktaler-3.00000036.exr:
Result:
file format version: 2, flags 0x0 Fraktaler3 (type string): "program = "fraktaler-3" version = "0-414-g61fa84a" location.real = "-1.748764520194788535" location.imag = "3e-13" location.zoom = "1.0000000e15" bailout.iterations = 262144 bailout.maximum_reference_iterations = 262144 bailout.maximum_perturb_iterations = 4096 bailout.inscape_radius = 0.000976562 image.width = 601 image.height = 67 image.subframes = 64 transform.reflect = true transform.exponential_map = true render.zoom_out_sequence = true opencl.tile_width = 601 opencl.tile_height = 67 " channels (type chlist): B, 32-bit floating-point, sampling 1 1 G, 32-bit floating-point, sampling 1 1 R, 32-bit floating-point, sampling 1 1 compression (type compression): zip, multi-scanline blocks dataWindow (type box2i): (0 0) - (600 66) displayWindow (type box2i): (0 0) - (600 66) lineOrder (type lineOrder): increasing y pixelAspectRatio (type float): 1 screenWindowCenter (type v2f): (0 0) screenWindowWidth (type float): 1 type (type string): "scanlineimage"
examples
editogv
edit- Now obsoleted by a fully automatic movie maker bash script (kfbtoogv.sh in the repository) - just specifiy the length in seconds and a couple of directories! A few things are hardcoded (program directories, antialiasing factor, ... - need to make them runtime options), and using ghc for simple maths is a bit overkill, so it's not perfect yet.
- Overnight I rendered a zoom out sequence of the same location at 3840x2160 into ~/work
- then I mkdir ~/work/tmp,
- kfbtoogv.sh 120 ~/work ~/work/tmp and 18mins later got this video without having to do anything manually:
full example
editmkdir ~/work
cd ~/work
wine32 ~/windows/fraktal_sft.exe
# render zoom out sequence with kalles fraktaler, saving to ~/work
for i in *.kfb ; do ~/code/maximus_kf-extras/pseudo-de < ${i} | pgmtoppm white > ${i%kfb}ppm done
# count number of kfb files, (ls *.kfb | tail -n 1) gives a hint, I had 311
ls *.kfb | ~/code/maximus_kf-extras/expmap 311 > expmap.pgm
ghc -e "100 / sqrt ( 565 * 19820 / (120 * 8000) )"
# or your favourite calculator program if you don't have ghc
# prints 29.27922435677391
# where 565 * 19820 is the size of the expmap
# 8000 is desired sample rate
# 120 is desired movie length in seconds
gimp expmap.pgm
# downscale by 29.27922435677391% (output from ghci)
# flip vertically
# make sure it's greyscale with no alpha
# save as expmap-downscaled.png
# gimp saves comments in its netpbm writers, my bad code doesn't handle it
pngtopnm < expmap-downscaled.png > expmap-downscaled.pgm
audacity
# import raw expmap-downscaled.pgm as raw unsigned 8bit PCM mono 8000Hz
# set project sample rate to 48000Hz
# select the track, mix and render
# select the track, apply filter DC Offset Removal (maybe needs a plugin)
# select the track, select amplify and apply to normalize the volume
# duplicate the track twice
# for each of the duplicates in turn, select it and apply a reverb (GVerb plugin)
# use slightly different reverb settings for each track (for stereo effect)
# normalize the duplicates by select track, select amplify, apply
# make one duplicate a left channel and the other a right channel
# set the levels of each track to -3dB
# select all tracks, mix and render
# normalize volume if needed
# select track, export as stereo wav 16bit PCM to expmap.wav
# note down the length (my test gave 1m56s)
ghc -e "1 * 60 + 56"
# to get the audio length in seconds (116)
# check the size of the kfb files (here I used 640 by 360)
# remember we have 311 kfb files
ls 00*.ppm | tac | xargs cat | ~/code/maximus_book/code/zoom 640 360 311 116 |
avconv -f yuv4mpegpipe -i - -i expmap.wav -acodec vorbis -ab 192k -vb 2M out.ogv
# finally!
mplayer out.ogv
expmap_wire
edit- parameters: http://mathr.co.uk/mandelbrot/2014-12-17_kf-extras_expmap_wire.kfr
- rendered from 98 640x360 kfb files, giving a 565x6075 image, downscaled 4x in GIMP for anti-aliasing.[2]
description
edit"How it works isn't too complex (most of the effort I put into making it stream data so it doesn't run out of memory for huge zoom sequences). The key loop that converts the flat image into a log-polar image runs like this:"
#pragma omp parallel for
for (int k = 0; k < expmap->circumference; ++k) {
double r = pow(0.5, 1.0 - expmap->row / (double) expmap->radius);
double t = 2 * pi * k / (double) expmap->circumference;
double i = r * cos(t) * expmap->height / 2.0 + expmap->width / 2.0;
double j = r * sin(t) * expmap->height / 2.0 + expmap->height / 2.0;
row[k] = kfb_get_nearest(expmap->kfb, i, j);
}
expmap->row++;
"Here width and height refer to the flat image, while circumference is the width of the output strip, and radius is the height of each section (one section is generated for each input file). The expmap->row variable is a counter that loops through concentric circles in the flat image, and there's some relatively simple maths to calculate the radius and angle for each point on the circle. Calculating the circumference and radius parameters is a bit more involved, here's the bit of code that does that with a few inline comments:"
expmap->circumference = expmap->height * pi / 2;
// 0.5 ^ (1 - 1 / radius) - 0.5 = |(0.5, 0)-(0.5*cos(t), 0.5*sin(t))|
// where t = 2 * pi / circumference
double t = 2 * pi / expmap->circumference;
double dx = 0.5 - 0.5 * cos(t);
double dy = 0.5 * sin(t);
double d = sqrt(dx * dx + dy * dy);
// (1 - 1 / radius) log 0.5 = log (d + 0.5)
// 1 - 1 / radius = log (d + 0.5) / log 0.5
// 1 / radius = 1 - log (d + 0.5) / log 0.5
expmap->radius = 1.0 / (1.0 - log(d + 0.5) / log(0.5));
The idea behind the maths is that ideally we want a uniform pixel aspect ratio, with pixels as square as possible. There's probably a neater and more direct way of calculating this, but I got it working so stopped looking for a better solution. Then the main loop steps through one row at a time (loading successive kfb files as necessary), keeping the previous row around to do the pseudo-de colouring I described in the previous blog post.
key words
edit- flat image = image in linear-linear scale ( normal Cartesian coordinate) = standard flat view of part of the Mandelbrot set. Here width and height refer to the flat image.
- log-polar image ( expmap )
- circumference is the width of the output strip
- radius is the height of each section (one section is generated for each input file
- The expmap->row variable is a counter that loops through concentric circles in the flat image circumference and radius parameters is a bit more involved, here's the bit of code that does that with a few inline comments
- exponential map EXR keyframe sequences
- expmap
- exponential map coordinate transformation (useful for export to zoomasm for efficient video assembly)
- exponentially spaced rings around the zoom center. See image exponential_strip.svg