snibgo's ImageMagick pages

Fractal noise animations

Fractal or Perlin noise can be animated.

This page follows on directly from Fractal noise, which follows on less directly from Multi-scale pyramids. See those pages for detailed explanations and references. This page is concerned only with animating fractal noise.

This page contains animated GIF files that are large (in megabytes).

The method

The script fractNoise.bat includes a simple mechanism for animation. At frame zero, it creates noise grids and writes them to files. At other frames, it reads these noise grid files and applies transformations. This page uses ImageMagick to make animated GIFs. Other tools such as ffmpeg may be used instead.

After the first frame (frame zero) is created, we don't need to add new noise for successive frames. All the frames are actually slight variations of each other, created from the same noise grid images.

By default, fractNoise.bat doesn't create animations. If you want animations, set fnA_FRM_NUM to a number. This is a frame number such as 0 (zero) for the first frame in a sequence, 1 (one) for the next, and so on. The script uses the frame number to calculate the variation to apply. Frame numbers don't need to be integers, and successive frame numbers don't need to be consecutive. After generating frame 0, you can create frames 1 to 500, or just 481 to 500. The corresponding final frames in both sequences will be identical

Frame 0 is where randomness is created. fractNoise.bat adds no more randomness for other frames, although another script could add randomness to the noise grids.

Changes between frames can occur equally for all octaves, or can depend on the noise frequency. For example, high-frequency noise might translate (change position) more quickly than low-frequency noise. This is determined by power factors, which works in the same way as the power factor for amplitude in static images. However, amplitude power factors are generally positive (so lower frequency noise dominates) where change power factor tend to be negative (so higher frequency noise moves fastest).

A single power factor is used for colour, translation, and zoom. This could be separated, so we could then have high frequencies moving more rapidly than low frequencies, but changing colour more slowly.

Within fractNoise.bat, animation is controlled by environment variables. When a variable is not set, a default value is used.

Variable Description
fnA_FRM_NUM Frame number, typically an integer. If blank, no animation.
fnA_COL Percentage colour change per frame. 100%==0%.
Default: 0
fnA_DX Horizontal displacement per frame, pixels, positive=pan right (image moves left).
Default: 0
fnA_DY Vertical displacement per frame, pixels, positive=pan down (image moves up).
Default: 0
fnA_ZM Zoom factor per frame, eg 0.998. Less than one zooms out, shrinking the image.
Default: 1
fnA_POW_FAC Power factor for animation.
0: all noise frequencies move at same speed
<0: higher frequencies move faster
>0: lower frequencies move faster
Default: 0
fnA_COLCYCMETH Colour cycling method.
Default: if no animation, null; otherwise sinarc2o

Future: we could separate fnA_COL into three; one per channel. Useful for fractal noise displacements?

fnA_POW_FAC can be used to ensure long cycles. For example, a power factor of 0.0 will move all octaves at the same speed. When one completes a cycle, they all do. But a power factor of 0.0001 will ensure than when the slowest completes, the next slowest will have completed blah cycles. Those two will complete at the same time only after blah cycles of the slowest.

Animations on ths page are created by my Windows laptop at about one frame per second. Most of that time, the script is doing the complicated work required to build the magick command, which takes only about 0.2 seconds.

For animation that includes changing colours, noise types other than "Random" (the default) are not recommended.

Example frames

Ensure enviroment variable are not set:

call %PICTBAT%fn_zeroEnvVarAnim

We define a seed, so the same examples will be created each time this page is built.

set fnSEED=1234

We create some frames from an animation that pans right (so the image moves left) by 6 pixels per frame.

Frame zero.

set fnA_FRM_NUM=0

call %PICTBAT%fractNoise ^
  . . . . . . fna_exfr0.png
fna_exfr0.pngjpg

Make frame 10.

set fnA_DX=6
set fnA_FRM_NUM=10

call %PICTBAT%fractNoise ^
  . . . . . . fna_exfr10.png

We have panned right by 6*10=60 pixels.
(The image has moved left,)

fna_exfr10.pngjpg

Make frame 50.

set fnA_DX=6
set fnA_FRM_NUM=50

call %PICTBAT%fractNoise ^
  . . . . . . fna_exfr50.png

We have panned right by 6*50=300 pixels.

fna_exfr50.pngjpg

Make frame 100.

set fnA_DX=6
set fnA_FRM_NUM=100

call %PICTBAT%fractNoise ^
  . . . . . . fna_exfr100.png

We have panned right by 6*100=600 pixels.
We are back where we started.

fna_exfr100.pngjpg

Reset the varables.

set fnA_FRM_NUM=
set fnA_DX=

Frame 100 is identical to frame zero:

%IMG7%magick ^
  fna_exfr0.png ^
  fna_exfr100.png ^
  -metric RMSE ^
  -format %%[distortion] -compare ^
  info: 
0

The same effect could almost have been obtained with IM's -roll operation. This is because all octaves have moved at the same speed.

As we might expect, the left half of frame 50 looks the same as the right half of frame zero, and the right half of frame 50 looks the same as the left half of frame 100 (which is also the left half of frame zero).

These look the same, but they are not. Frame zero is clearly not tilable, so rolling the image left and moving the pixels that drop off the left to the right shouldn't work.

We can show this by chopping frame zero in half, swapping and appending. Then we can compare this to frame 50.

Reverse frame zero.

%IMG7%magick ^
  fna_exfr0.png ^
  -crop 2x1@ +repage ^
  +swap ^
  +append +repage ^
  fna_exfr0rev.png

The line between the halves is clear.

fna_exfr0rev.pngjpg

Blink comparator between
reversed frame zero, and frame 50.

%IMG7%magick ^
  -loop 0 -delay 50 ^
  fna_exfr0rev.png ^
  fna_exfr50.png ^
  fna_exfr0r50.gif

The frames are similar, but clearly different.

fna_exfr0r50.gif

I regard this as almost magical. We take a non-tilable image and scroll it left until it is back where it started. The process isn't quite perfect. If we look closely at the central vertical of frame 50, or the column 60 pixels from the right of frame 10, we can see a small discontinuity.

We can move octaves at different speeds:

Pan the highest frequency right at 6 pixels per frame;
make frame 100.

set fnA_DX=6
set fnA_POW_FAC=-1
set fnA_FRM_NUM=100

set fnTXT_OUT=1

call %PICTBAT%fractNoise ^
  . . . . . . fna_exfr5.png >fna_exfr5.lis

We have panned right highest frequency by 6*100=600 pixels.
We are back where we started for that frequency only.

fna_exfr5.pngjpg

Reset the varables.

set fnTXT_OUT=

set fnA_FRM_NUM=
set fnA_POW_FAC=
set fnA_DX=

Here is fna_exfr5.lis, the text output from the fractNoise.bat script.

 f:\prose\PICTURES>rem Make fractal noise. 
F:\pictures\fnInit: 600 400 1 300 2 0
fnA_FRM_NUM=100 ZM_F=1 DX_F=600 DY_F=0
.   AN_AMP.0=1 sANIM=  ( -clone 0      -function sinusoid 0.5,-90,0.5,0.5      -function arcsin 1   )   ( -clone 0      -function sinusoid 0.5,270,0.5,0.5      -function arcsin 1   )   ( -clone 0      -evaluate And 1 -fill White +opaque Black   )   -delete 0   -compose Over -composite -virtual-pixel tile -distort SRT 600,0,1,0,0,0
fnA_FRM_NUM=100 ZM_F=1 DX_F=600 DY_F=0
.   AN_AMP.1=0.500000000000002 sANIM=  ( -clone 0      -function sinusoid 0.5,-90,0.5,0.5      -function arcsin 1   )   ( -clone 0      -function sinusoid 0.5,270,0.5,0.5      -function arcsin 1   )   ( -clone 0      -evaluate And 1 -fill White +opaque Black   )   -delete 0   -compose Over -composite -virtual-pixel tile -distort SRT 150.000000000001,0,1,0,0,0
fnA_FRM_NUM=100 ZM_F=1 DX_F=600 DY_F=0
.   AN_AMP.2=0.250000000000001 sANIM=  ( -clone 0      -function sinusoid 0.5,-90,0.5,0.5      -function arcsin 1   )   ( -clone 0      -function sinusoid 0.5,270,0.5,0.5      -function arcsin 1   )   ( -clone 0      -evaluate And 1 -fill White +opaque Black   )   -delete 0   -compose Over -composite -virtual-pixel tile -distort SRT 37.5000000000002,0,1,0,0,0
fnA_FRM_NUM=100 ZM_F=1 DX_F=600 DY_F=0
.   AN_AMP.3=0.125000000000001 sANIM=  ( -clone 0      -function sinusoid 0.5,-90,0.5,0.5      -function arcsin 1   )   ( -clone 0      -function sinusoid 0.5,270,0.5,0.5      -function arcsin 1   )   ( -clone 0      -evaluate And 1 -fill White +opaque Black   )   -delete 0   -compose Over -composite -virtual-pixel tile -distort SRT 9.37500000000007,0,1,0,0,0
fnA_FRM_NUM=100 ZM_F=1 DX_F=600 DY_F=0
.   AN_AMP.4=0.0625000000000003 sANIM=  ( -clone 0      -function sinusoid 0.5,-90,0.5,0.5      -function arcsin 1   )   ( -clone 0      -function sinusoid 0.5,270,0.5,0.5      -function arcsin 1   )   ( -clone 0      -evaluate And 1 -fill White +opaque Black   )   -delete 0   -compose Over -composite -virtual-pixel tile -distort SRT 2.37500000000001,0,1,0,0,0
fnA_FRM_NUM=100 ZM_F=1 DX_F=600 DY_F=0
.   AN_AMP.5=0.0312500000000001 sANIM=  ( -clone 0      -function sinusoid 0.5,-90,0.5,0.5      -function arcsin 1   )   ( -clone 0      -function sinusoid 0.5,270,0.5,0.5      -function arcsin 1   )   ( -clone 0      -evaluate And 1 -fill White +opaque Black   )   -delete 0   -compose Over -composite -virtual-pixel tile -distort SRT 0.593750000000002,0,1,0,0,0
fnA_FRM_NUM=100 ZM_F=1 DX_F=600 DY_F=0
.   AN_AMP.6=0.0156250000000001 sANIM=  ( -clone 0      -function sinusoid 0.5,-90,0.5,0.5      -function arcsin 1   )   ( -clone 0      -function sinusoid 0.5,270,0.5,0.5      -function arcsin 1   )   ( -clone 0      -evaluate And 1 -fill White +opaque Black   )   -delete 0   -compose Over -composite -virtual-pixel tile -distort SRT 0.140625000000001,0,1,0,0,0
fnA_FRM_NUM=100 ZM_F=1 DX_F=600 DY_F=0
.   AN_AMP.7=0.00781250000000003 sANIM=  ( -clone 0      -function sinusoid 0.5,-90,0.5,0.5      -function arcsin 1   )   ( -clone 0      -function sinusoid 0.5,270,0.5,0.5      -function arcsin 1   )   ( -clone 0      -evaluate And 1 -fill White +opaque Black   )   -delete 0   -compose Over -composite -virtual-pixel tile -distort SRT 0.0390625000000002,0,1,0,0,0
fnA_FRM_NUM=100 ZM_F=1 DX_F=600 DY_F=0
.   AN_AMP.8=0.00390625000000001 sANIM=  ( -clone 0      -function sinusoid 0.5,-90,0.5,0.5      -function arcsin 1   )   ( -clone 0      -function sinusoid 0.5,270,0.5,0.5      -function arcsin 1   )   ( -clone 0      -evaluate And 1 -fill White +opaque Black   )   -delete 0   -compose Over -composite -virtual-pixel tile -distort SRT 0.00781250000000002,0,1,0,0,0
levels 0: %[fx:minima] %[fx:maxima]
levels 1: %[fx:minima] %[fx:maxima]
levels 1: %[fx:minima] %[fx:maxima]
levels 2: %[fx:minima] %[fx:maxima]
levels 2: %[fx:minima] %[fx:maxima]
levels 3: %[fx:minima] %[fx:maxima]
levels 3: %[fx:minima] %[fx:maxima]
levels 4: %[fx:minima] %[fx:maxima]
levels 4: %[fx:minima] %[fx:maxima]
levels 5: %[fx:minima] %[fx:maxima]
levels 5: %[fx:minima] %[fx:maxima]
levels 6: %[fx:minima] %[fx:maxima]
levels 6: %[fx:minima] %[fx:maxima]
levels 7: %[fx:minima] %[fx:maxima]
levels 7: %[fx:minima] %[fx:maxima]
levels 8: %[fx:minima] %[fx:maxima]
levels 8: %[fx:minima] %[fx:maxima]
levels before AL: %[fx:minima] %[fx:maxima]
Created fna_exfr5.png

In that output, sANIM is the animation component, which includes "-distort SRT". For the 100th frame, the highest frequency moves by 600 pixels. The next moves by only 150 pixels, but its blocksize is 300x200, so it will be resized by x2 and will move by 300 pixels after the resize. And so on down to the highest freuency, where the SRT movement is 0.0390625 in a block size of 5x3. It will resize x120, so will move by 4.6875 pixels after the resize.

Calculating more directly, the animation power factor is -1, and there are eight octaves, so 600*pow(0.5,7) = 4.6875.

Creating an animation

We create animations by calling fractNoise.bat in a loop to make each frame, then convert all the frames into a single animated gif.

We number frames from zero, and use leading zeros in filenames so they sort lexicographically. For the GIF, we set the delay to 20 (hundredths of a second), so we can observe changes.

We put this in a script, fnAnim.bat. The second parameter (%2) to this script is a quoted string of the first six parameters that will be passed to fractNoise.bat. If we want all these six to be defaults, we can simply put a dot (".").

By default, we create just five frames.

The following examples all use default parameters to fractNoise.bat, so all frequencies have the same weighting within each frame. However, animated changes between frames may be faster for high or low frequencies.

Default parameters: no animation.

call %PICTBAT%fn_zeroEnvVarAnim
set fnSEED=1234

call %PICTBAT%fnAnim ^
  . . fna_a_1
fna_a_1.gif

Change colours by 1% of a complete cycle per frame.

call %PICTBAT%fnAnim ^
  . . fna_a_2 1

The change at each frame is barely visible,
so the visual effect is of a new image every second,
instead of the same image being repeated every second.

fna_a_2.gif

Displace horizontally 3 pixels/frame.

call %PICTBAT%fnAnim ^
  . . fna_a_3 . 3
fna_a_3.gif

Displace vertically 3 pixels/frame.

call %PICTBAT%fnAnim ^
  . . fna_a_4 . . 3
fna_a_4.gif

Displace horizontally,
with the highest frequencies moving at -3 pixels/frame.

call %PICTBAT%fnAnim ^
  . . fna_a_5 . -3 . . -1
fna_a_5.gif

Animation is cyclic: it eventually reaches where it started. Each element has its own cycle length. When the power factor is an integer, all cycles reach the start when the slowest cycle does so. Meanwhile, each faster cycle will have repeated an integer number of times.

In general, the overall cycle length in frames is the least common multiple of the individual cycle lengths.

Blah: changing colours. See Color cycling.

Animated zoom

For animated zoom-out, integer power factors will create repeated patterns.

Technical aside.

For movies of explosions with billowing fire and smoke, we need to expand the image, which is equivalent to zooming in.

Animated zoom presents a problem. Of course, we could simply enlarge a fractal noise image. But the image is supposed to contain as much detail as it ever had, however far we zoom in, and enlarging an image won't do this. Eventually we will zoom into a single pixel, which has no noise at all. In theory, we could add compensatory noise to each octave at each frame. But the amount of added noise is critical, as too much or too little will accumulate between frames, quickly turning into chaos or gray mush.

A better solution seems to be to always zoom out, making the image smaller at each frame, using -virtual-pixel tile to introduce new noise at the edges. (We can always reverse the sequence later if we want.) Iterating a zoom-out doesn't work well, as edge pixels are repeatedly mixed, causing gray mush that propagates inwards. Instead, we keep the original and zoom out from it.

We can't zoom out of the fracatal noise image itself. The introduced noise wouldn't be fractal unless we used -virtual-pixel mirror or -virtual-pixel tile. Mirror would introduce pattern, and tile would introduce both pattern and discontinuity. Instead, we zoom out of each noise grid, before resizing it to the full size and adding the results together.

With this scheme, fractNoise.bat is called once to create a set of noise grid images. The fractNoiseAdd.bat is called to zoom out of each, creating a new and temporary set of noise grid images, which fractNoise.bat reads for the next frame.

At each frame, the same noise grid images are read, but the amount zoomed out progressively increases. Zooming out is performed by -distort SRT because we need its ability to use virtual pixels, and for resizing at factors that are not pixel boundaries. The default filter introduces an interference pattern, visible as dark rectangles that quickly move towards the centre, but -filter Lanczos works well.

Sadly, zooming out with -distort SRT is slow, and becomes slower with greater reductions. We are talking minutes per frame, even at web sizes. I suppose the problem is EWA. (Perhaps "`-filter point`" would help.)

Currently, noise grids are made no larger than required. Instead, they could be made at least, say, 256x256. This would increase the cycle lengths of panning and zooming. It would need more data to be passed from the construction of frame 0.

Calling fractNoise.bat multiple times will generate independent images, totally different from each other. For animation, we want slight changes between frames. For this, we can tell fractNoise.bat to write noise grid files. Then we add noise to each noise grid file with the script fractNoiseAdd.bat. Then we run fractNoise.bat again, but telling it to read the noise grids from files instead of generating new noise.

For each frame, we add a little noise to the noise grid files, and create another fractal noise image.

The animation should run at 5 frames/second, so it changes gradually over six seconds, then jumps back to where it started. However, observing the gradual change is difficult, so we have the illusion of an image that remains constant for six seconds, then sudden switches to a different image.

Can we do a never-ending zoom? When we zoom into noise, the overall image becomes less noisy.

Aside: Does ffmepg have an auto-level? Yes, like this: %IMG7%ffmpeg -i fn_fr_%06d.png -vf pp=al:f f.mpg. See Ffmpeg Filters Documentation: pp.

Animated displacement

Instead of animating a fractal image that slowly changes, we can use the changing fractal as a displacement map on a static image, and animate the results.

Above is an animation where each frame is fractal noise. Here, each frame is a static image that is displaced by fractal noise, using the same technique that was used above for a still image of fire, fn_fire.png.

The script fnAnim2.bat does this.

For example, we create 20 frames where the something changes by 5% per frame, so the GIF contains one complete cycle.

Change the colour by 5% per frame.

call %PICTBAT%fn_zeroEnvVarAnim
set fnSEED=1234

set fnA_DISPLACE_ARGS=10x10

call %PICTBAT%fnAnim2 ^
  20 ^
  "10 40 1.2 ." ^
  fna_a_toes_1 5 . . . . ^
  toes.png
fna_a_toes_1.gif

Move the map left by 5% per frame.

set fnA_DISPLACE_ARGS=10x10

call %PICTBAT%fnAnim2 ^
  20 ^
  "10 40 1.2 ." ^
  fna_a_toes_2 . 5c . . . ^
  toes.png
fna_a_toes_2.gif

Move the map up by 5% per frame.

set fnA_DISPLACE_ARGS=10x10

call %PICTBAT%fnAnim2 ^
  20 ^
  "10 40 1.2 ." ^
  fna_a_toes_3 . . 5c . . ^
  toes.png
fna_a_toes_3.gif

Change the colour by 5% per frame;
apply sin & cos to map.

set fnA_DISPLACE_ARGS=10x10
set fnA_SINCOS=1

call %PICTBAT%fnAnim2 ^
  20 ^
  "10 40 1.2 ." ^
  fna_a_toes_4 5 . . . . ^
  toes.png

set fnA_SINCOS=
set fnA_DISPLACE_ARGS=
fna_a_toes_4.gif

As previous, but supersampling vertically.

set fnA_DISPLACE_ARGS=10x10
set fnA_SINCOS=1

%IMG7%magick ^
  toes.png ^
  -resize 100%%x200%% ^
  fn_toes_l.miff

call %PICTBAT%fnAnim2 ^
  20 ^
  "10 40 1.2 ." ^
  fna_a_toes_5 5 . . . . ^
  fn_toes_l.miff

%IMG7%magick ^
  fna_a_toes_5.gif ^
  -resize 100%%x50%% ^
  fna_a_toes_5s.gif

set fnA_SINCOS=
set fnA_DISPLACE_ARGS=

Water ripples?
Instead of a simple resize,
we could do perspective distortions
to get narrower ripples at the top.

fna_a_toes_5s.gif

Animated fire

As always on this page, the GIFs run at 5 frames/second so we can see what is happening. However, these have more frames.

The files fn_fire_base.png and fn_fire.png were created in Fractal noise: displacements.

Use the same fractal noise parameters that
were used to create fn_fire.png.

call %PICTBAT%fn_zeroEnvVarAnim
set fnSEED=1234

set fnGRAY=1

call %PICTBAT%fnAnim2 ^
  10 ^
  "10 40 1.1 ." ^
  fna_a_fire_1 3 . 5 . -0.5 ^
  fn_fire_base.png

set fnGRAY=
fna_a_fire_1.gif

Use different parameters,
the default parameters to fractNoise.bat,
so each frame has all frequences at equal amplitude
(but high frequencies move more quickly).

call %PICTBAT%fnAnim2 ^
  10 . fna_a_fire_2 3 . 5 . -0.5 ^
  fn_fire_base.png
fna_a_fire_2.gif

Instead of displacing a simple gradient image,
we can displace the fire image fn_fire.png.

call %PICTBAT%fnAnim2 ^
  10 . fna_a_fire_3 3 . 5 . -0.5 ^
  fn_fire.png

This creates many isolated pixels.
We could blur, or supersample.

fna_a_fire_3.gif

As previous, but supersampled.

set fnA_SUPER_SAMP=400

call %PICTBAT%fnAnim2 ^
  10 . fna_a_fire_4 3 . 5 . -0.5 ^
  fn_fire.png

set fnA_SUPER_SAMP=
fna_a_fire_4.gif

A complete cycle; an endless fire.

set fnA_DISPLACE_ARGS=20%%%%x50%%%%

call %PICTBAT%fnAnim2 ^
  100 . fna_a_fire_5 2 . 4 . 0 ^
  fn_fire.png

set fnA_DISPLACE_ARGS=
fna_a_fire_5.gif

The parameters that fnAnim.bat and fnAnim2.bat pass to fractNoise.bat are constant from one frame to the next. They could vary, and this variation could be noisy.

Animated clouds

In Fractal noise: designing for animation, a still image that resembled a cloud was created. We can animate this by displacing the noise clockwise and outwards. fractNoise.bat displaces in the x and y directions, which become tangential and radial directions after depolar.

set fnA_POW_FAC=-0.5 makes the high-frequency noise move faster than the low-frequency noise.

I have added an extra process to the post-processing. After unrolling, I displace the pixels by up to 4 pixels, using the same fractal noise image as a relative displacement map. This moves noise more chaotically than a simple spiral motion. As displacement is in integer units, this would introduce "jumping" of pixels, so I super-sample to reduce this. As the fractal noise image is greyscale, sin and cos are used to created differences in the x and y directions. Without sin and cos, all movement would be diagonal, visible as artifacts. As an alternative, a colour fractal noise image could be used.

call %PICTBAT%fn_zeroEnvVarAnim
set fnSEED=1234

set PREF=fna_cld
set FRAME_EXT=.miff

del %PREF%_a_??????%FRAME_EXT% 2>nul

set STEP=1

set NUM_FR=10
set /A LAST_FR=%NUM_FR%-1

set fnPREFIX=fna_cld_x
set fnGRAY=1

set fnA_DX=0.6
set fnA_DY=-1
set fnA_POW_FAC=-0.5
set fnTILE=1
set fnAUTO_LEVEL_END=0
set fnAUTO_GAM_END=0

set fnPROC_EACH=( +clone ^
  -colorspace Gray ^
  -shade 135x30 ) ^
-compose Hard_Light -composite

set fnPROC_EACH=

set SP=^
0,0,#000,^
%%[fx:w-1],0,#f00,^
0,%%[fx:h-1],#0f0,^
%%[fx:w-1],%%[fx:h-1],#ff0

set FNUM=0

for /L %%I in (0,1,%LAST_FR%) do (

  set fnA_FRM_NUM=%%I

  set LZ=000000!FNUM!
  set LZ=!LZ:~-6!

  call %PICTBAT%fractNoise ^
    600 600 . . . . %PREF%_a_!LZ!%FRAME_EXT%

  call %PICTBAT%exHvyBlr ^
    %PREF%_a_!LZ!%FRAME_EXT% ^
    . . %PREF%_a_!LZ!%FRAME_EXT%

  %IMG7%magick ^
    %PREF%_a_!LZ!%FRAME_EXT% ^
    +write mpr:FRACNSE ^
    ^( +clone ^
       -sparse-color bilinear ^
          "0,0,White 0,%%[fx:h-1],Black" ^
    ^) ^
    -compose Hard_Light -composite ^
    -distort polar -1,0 ^
    -resize 200%% ^
    ^( mpr:FRACNSE ^
       -resize 200%% ^
       -channel R -evaluate sin 1 ^
       -channel G -evaluate cos 1 ^
       +channel ^
    ^) ^
    -compose Displace ^
      -set option:compose:args 4x4 ^
      -composite ^
    -resize 50%% ^
    -level 50%%,100%% ^
    +write %PREF%_b_!LZ!%FRAME_EXT% ^
    ^( -size 1x100 gradient: -rotate 90 ^
       -evaluate Pow 0.5 ^
       -function Polynomial -1,2,0 ^
       -evaluate Pow 0.5 ^
    ^) ^
    -clut ^
    +write %PREF%_c_!LZ!%FRAME_EXT% ^
    ^( +clone ^
       -scale x20%% ^
       -fill Black -colorize 100 ^
    ^) ^
    +swap -append +repage ^
    -resize "100%%x157.08%%^!" ^
    ^( +clone ^
       -sparse-color bilinear "%SP%" ^
       -channel G ^
       -function Polynomial -1,0,1 ^
       -evaluate Pow 0.5 ^
       -negate ^
       +channel ^
    ^) ^
    -compose Distort ^
      -set option:compose:args 100%%x100%% ^
      -composite ^
    -resize "100%%x63.66%%^!" ^
    -gravity South -crop 600x600+0+0 ^
      +repage ^
    %PREF%_d_!LZ!%FRAME_EXT%

  set /A FNUM+=%STEP%
)

call %PICTBAT%frTrim %PREF%_a_*%FRAME_EXT% 1

%IMG7%magick ^
  -loop 0 -delay 20 ^
  %PREF%_a_*%FRAME_EXT% ^
  %PREF%_a.gif
fna_cld_a.gif
call %PICTBAT%frTrim %PREF%_b_*%FRAME_EXT% 1

%IMG7%magick ^
  -loop 0 -delay 20 ^
  %PREF%_b_*%FRAME_EXT% ^
  %PREF%_b.gif
fna_cld_b.gif
call %PICTBAT%frTrim %PREF%_c_*%FRAME_EXT% 1

%IMG7%magick ^
  -loop 0 -delay 20 ^
  %PREF%_c_*%FRAME_EXT% ^
  %PREF%_c.gif
fna_cld_c.gif
call %PICTBAT%frTrim %PREF%_d_*%FRAME_EXT% 1

%IMG7%magick ^
  -loop 0 -delay 20 ^
  %PREF%_d_*%FRAME_EXT% ^
  %PREF%_d.gif

del %PREF%_?_*%FRAME_EXT%

set fnGRAY=
fna_cld_d.gif

Animation with tiling

Blah.

Future

Other noise types

How do we cycle colours with other noise types, eg Gaussian? Maybe we always use Random, but have options to process this via a clut to make other distributions. (Or just do that with fnPROC. Examples please.) Abs should leave result either as 50-100% or 0-100%. Maybe also -negate?

Performance

Frames are independent of each other: once the noise grids and weights are calculated, all the frames could be generated simultaneously, so parallel processing could be fairly simple and yield a large performance increase.

The animations on this page took about 1.5 seconds per frame to generate, which is mostly simple processing in the script. As with static images, translating the script to C (or a faster script language) could result in significant improvement.

When creating frame zero, fractNoise.bat could write results (eg noise grid sizes and weights) to a file. Then no need to re-calculate. However, nor could weights be modified during the animation.

Non-linear motion

Distort time?

Blah.

Weighting by clut

Just as the only control for the weighting of amplitudes of octaves is by a power factor on the frequency, the same applies to weighting of animation speed. The same possible future development applies: I might implement weighting by clut.

This would permit negative weighting, as animation is cyclic, eg movement of -10% is exactly the same as movement by 90%.

Cycle length

It would be useful if the script could tell us how many frames made a complete cycle, and how many frames made a cycle of the most obvious movement, etc.

Cleanup

del fna_*.miff
del fna_*0000*.png

call %PICTBAT%fn_zeroEnvVar

Scripts

For convenience, .bat scripts are also available in a single zip file. See Zipped BAT files.

fractNoise.bat

rem Make fractal noise.

@rem %1 %2 Width, height in pixels.
@rem %3 minimum block size in pixels. [1]
@rem %4 maximum block size in pixels. [min(w,h)/2]
@rem %5 block factor: Block size is increased by this factor from min to max. >=1 [2]
@rem %6 power factor for amplitude proportional to frequency. [Default 0.0]
@rem   0 for all frequencies have same amplitude;
@rem   > 0 (eg 1, 1.5) for highest frequencies have lowest amplitude.
@rem %7 output file. [fn.png]

@rem Returns fnNUM_OCTAVES number of octaves.

@rem Also uses:
@rem
@rem   fnSEED Seed for pseudo-random generator. [Default: no seed]
@rem   fnGRAY if 1, creates grayscale output, otherwise RGB.
@rem   fnNOISE_TYPE Noise type: random, gaussian, uniform, etc.
@rem   fnABS_NSE if 1, takes absolute value of noise.
@rem   fnFILTER Resize filter.
@rem   fnIM location of IM's "magick" to be used.
@rem   fnPREFIX prefix for working files (but not output file). [fn_]
@rem   fnNG_EXT extension for noise grid files. [.miff]
@rem   fnLINEAR if 1, converts noise to linear, the result to non-linear.
@rem   fnAUTO_LEVEL_EACH whether to apply auto-levelling to each octave. [0]
@rem   fnAUTO_LEVEL_END whether to apply auto-levelling at the end. [1]
@rem     0 no auto-levelling,
@rem     1 synchronised auto-levelling,
@rem     2 unsynchronised (more colouful) auto-levelling.
@rem     ? Equalize
@rem     ? Gaussianise
@rem   fnAUTO_GAM_END whether to apply auto-gamma at the end. [1]
@rem   fnRD_PYR 0 or 1, override automatic choice about reading pyramid files instead of creating them.
@rem   fnWR_PYR 0 or 1, override automatic choice about writing pyramid files.
@rem   fnPROC_EACH a process to be applied to each octave.
@rem   fnTXT_OUT if 1, writes some text data.
@rem
@rem   fnA_FRM_NUM frame number, typically an integer. If blank, no animation.
@rem Animation control.
@rem   fnA_COL percentage colour change per frame. 100%==0%. [0]
@rem   fnA_DX horizontal displacement per frame, pixels, positive=pan right (image moves left). [0]
@rem   fnA_DY vertical displacement per frame, pixels, positive=pan down (image moves up). [0]
@rem   fnA_ZM zoom factor per frame, eg 0.998. <1 zooms out, shrinks image. [1]
@rem   fnA_POW_FAC power factor for animation. [0]
@rem   fnA_COLCYCMETH colour cycling method. [null or sinarc2o]
@rem
@rem
@rem First release: 28 September 2015.
@rem
@rem Updated:
@rem   24-September-2022 for IM v7.
@rem

@rem TODO: power defined by clut image file, so we can emphasise middle frequencies.


@setlocal enabledelayedexpansion

@call echoOffSave


set pyGRAPHIC=0
set pyWEIGHTING=sum

set OUTFILE=%7
if "%OUTFILE%"=="." set OUTFILE=
if "%OUTFILE%"=="" set OUTFILE=fn.png

call %PICTBAT%fnInit %1 %2 %3 %4 %5 %6
if ERRORLEVEL 1 exit /B 1

call %PICTBAT%fnOne %OUTFILE%
if ERRORLEVEL 1 exit /B 1


call echoRestore

@endlocal & set fnOUTFILE=%OUTFILE%&set fnPYR_FILE=%fnPYR_FILE%& set fnWW=%fnWW%& set fnHH=%fnHH%& set fnNUM_OCTAVES=%NUM_OCTAVES%& set fnPOW_FAC=%POW_FAC%

frMinMax.bat

Auto-levelling each frame, then assembling into an animation, causes flicker. Instead, we we want to level all the frames by the same amount such that the minimum value across all frames is zero and the maximum is 100%. For this, we could use -evaluate-sequence twice:

for /F "usebackq" %%L in (`%IMG7%magick ^
  fn_fr_*%FRAME_EXT% ^
  -evaluate-sequence Min ^
  -format "MIN=%%[fx:100*minima]" ^
  info:`) do set %%L

for /F "usebackq" %%L in (`%IMG7%magick ^
  fn_fr_*%FRAME_EXT% ^
  -evaluate-sequence Max ^
  -format "MAX=%%[fx:100*maxima]" ^
  info:`) do set %%L

That would rely on being able to read all the frames into memory. Instead, I use a script frMinMax.bat to loop through the images separately.

rem Finds min and max values of %1, a wildcarded filename eg frames_*.png.
@rem
@rem %2 if 1, also levels each image accordingly.
@rem
@rem Returns:
@rem   fmmMIN minimum value found in all pixels of all frames.
@rem   fmmMAX maximum value found in all pixels of all frames.
@rem   fmmMIN_PC minimum as a percentage.
@rem   fmmMAX_PC maximum as a percentage.
@rem
@rem Updated:
@rem   24-September-2022 for IM v7.
@rem

@if "%1"=="" findstr /B "rem @rem" %~f0 & exit /B 1

@setlocal enabledelayedexpansion

@call echoOffSave

call %PICTBAT%setInOut %1 fmm


set DO_LEV=%2
if "%DO_LEV%"=="." set DO_LEV=
if "%DO_LEV%"=="" set DO_LEV=0

if "%fmmIM%"=="" set fmmIM=%IMG7%

set PREC=-precision 15

set MIN=9999
set MAX=-9999
for %%F in (%1) do (
  for /F "usebackq" %%L in (`%fmmIM%magick ^
    %%F ^
    %PREC% ^
    -format "MIN=%%[fx:min(!MIN!,minima)]\nMAX=%%[fx:max(!MAX!,maxima)]" ^
    info:`) do set %%L
)

for /F "usebackq" %%L in (`%fmmIM%magick identify ^
  %PREC% ^
  -format "MIN_PC=%%[fx:!MIN!*100]\nMAX_PC=%%[fx:!MAX!*100]" ^
  xc:`) do set %%L

if "%MIN%"=="9999" echo %0: no images & exit /B 1

if "%DO_LEV%"=="1" (
  for %%F in (%1) do %fmmIM%magick ^
    %%F ^
    -level %MIN_PC%%%,%MAX_PC%%% ^
    %%F
)

call echoRestore

@endlocal & set fmmMIN=%MIN%& set fmmMAX=%MAX%& set fmmMIN_PC=%MIN_PC%& set fmmMAX_PC=%MAX_PC%

frTrim.bat

This finds (and applies, if %2 is 1) the least-aggressive trim that can be applied to frames in a set.

rem Finds trim values of %1, a wildcarded filename eg frames_*.png.
@rem
@rem %2 if 1, also trims each image accordingly.
@rem %3 fuzz percentage value. [0]
@rem
@rem Updated:
@rem   24-September-2022 for IM v7.
@rem

@if "%1"=="" findstr /B "rem @rem" %~f0 & exit /B 1

@setlocal enabledelayedexpansion

@call echoOffSave

call %PICTBAT%setInOut %1 ftr


set DO_TRIM=%2
if "%DO_TRIM%"=="." set DO_TRIM=
if "%DO_TRIM%"=="" set DO_TRIM=0

set FUZZ_PC=%3
if "%FUZZ_PC%"=="." set FUZZ_PC=
if "%FUZZ_PC%"=="" set FUZZ_PC=0

if %FUZZ_PC%==0 (
  set sFUZZ=
) else (
  set sFUZZ=-fuzz %FUZZ_PC%%%%%
)

if "%ftrIM%"=="" set ftrIM=%IMG7%

set XL=9999999
set XT=9999999
set XR=-1
set XB=-1

for %%F in (%1) do (
  for /F "usebackq tokens=1-4 delims=x+" %%A in (`%ftrIM%magick ^
    %%F ^
    %sFUZZ% ^
    -format "%%@" ^
    info:`) do (
    set L=%%C
    set T=%%D
    set /A R=%%C+%%A
    set /A B=%%D+%%B

echo !L! !T! !R! !B!

    if !XL! GTR %%C set XL=%%C
    if !XT! GTR %%D set XT=%%D
    if !XR! LSS !R! set XR=!R!
    if !XB! LSS !B! set XB=!B!
  )
)

echo XL=%XL% XT=%XT% XR=%XR% XB=%XB%

set /A W=%XR%-%XL%
set /A H=%XB%-%XT%

set sTRIM=%W%x%H%+%XL%+%XT%

echo sTRIM=%sTRIM%

if "%DO_TRIM%"=="1" (
  for %%F in (%1) do %fmmIM%magick ^
      %%F ^
      -gravity NorthWest ^
      -crop %sTRIM% +repage ^
      %%F
)

call echoRestore

@endlocal & set ftrTRIM=%sTRIM%

fnAnim.bat

rem Animated fractal noise.

@rem %1 number of frames [5]
@rem %2 quoted string of first six parameters to fractNoise.bat [". . . . . ."]
@rem %3 prefix for frames and output gif [fna_]
@rem %4 colour variation percent per frame [0]
@rem %5 dx, pixels per frame [0]
@rem %6 dy, pixels per frame [0]
@rem %7 zoom factor per frame eg 0.998 [1]
@rem %8 power factor: 0 = all frequencies change equally. [0]
@rem
@rem Also uses:
@rem   fnA_STEP step for frame numbers given to fractNoise.
@rem
@rem Updated:
@rem   24-September-2022 for IM v7.
@rem

@rem No compulsory parameters
@rem if "%1"=="" findstr /B "rem @rem" %~f0 & exit /B 1

@setlocal enabledelayedexpansion

@call echoOffSave

call %PICTBAT%setInOut %1 fa


set NUM_FR=%1
if "%NUM_FR%"=="." set NUM_FR=
if "%NUM_FR%"=="" set NUM_FR=5

set FN_PARAM=%~2
if "%FN_PARAM%"=="." set FN_PARAM=
if "%FN_PARAM%"=="" set FN_PARAM=. . . . . .

set PREF=%3
if "%PREF%"=="." set PREF=
if "%PREF%"=="" set PREF=fna_

set fnA_COL=%4
if "%fnA_COL%"=="." set fnA_COL=
if "%fnA_COL%"=="" set fnA_COL=0

set fnA_DX=%5
if "%fnA_DX%"=="." set fnA_DX=
if "%fnA_DX%"=="" set fnA_DX=0

set fnA_DY=%6
if "%fnA_DY%"=="." set fnA_DY=
if "%fnA_DY%"=="" set fnA_DY=0

set ZM=%7
if "%ZM%"=="." set ZM=
if "%ZM%"=="." set ZM=1

set fnA_POW_FAC=%8
if "%fnA_POW_FAC%"=="" set fnA_POW_FAC=
if "%fnA_POW_FAC%"=="" set fnA_POW_FAC=0

if "%fnA_STEP%"=="" set fnA_STEP=1


echo %~n0: FN_PARAM=%FN_PARAM%


set fnAUTO_LEVEL_EACH=0
set fnAUTO_LEVEL_END=0

set FRAME_EXT=.miff
rem set fnTXT_OUT=1

set /A LAST_FR=%NUM_FR%-1

del %PREF%_??????%FRAME_EXT% 2>nul

set fnA_FRM_NUM=0
call %PICTBAT%fnInit %FN_PARAM%
if ERRORLEVEL 1 (
  echo %0: fnInit failure
  exit /B 1
)

set FNUM=0

for /L %%I in (0,1,%LAST_FR%) do (

  set fnA_FRM_NUM=%%I

  set LZ=000000!FNUM!
  set LZ=!LZ:~-6!

  call %PICTBAT%fnOne ^
    %PREF%_!LZ!%FRAME_EXT%

  if ERRORLEVEL 1 (
    echo %0: fnOne failure
    exit /B 1
  )

  echo %~n0: %fnOUTFILE%

  set /A FNUM+=%fnA_STEP%

)

call %PICTBAT%frMinMax %PREF%_??????%FRAME_EXT% 1
if ERRORLEVEL 1 exit /B 1

call %PICTBAT%frGamma %PREF%_??????%FRAME_EXT% . 1
if ERRORLEVEL 1 exit /B 1

%IMG7%magick ^
  -loop 0 -delay 20 ^
  %PREF%_*%FRAME_EXT% ^
  %PREF%.gif


call echoRestore

@endlocal & set faOUTFILE=%OUTFILE%

fnAnim2.bat

rem Animated fractal noise.
rem Given image %9, makes frames with %9 displaced by varying fractal noise.

@rem %1 number of frames [5]
@rem %2 quoted string of four parameters to fractNoise.bat [". . . ."]
@rem   (width and height are determined by %9.)
@rem %3 prefix for frames and output gif [fna2_]
@rem %4 colour variation percent per frame [0]
@rem %5 dx, pixels per frame [0]
@rem %6 dy, pixels per frame [0]
@rem %7 zoom factor per frame eg 0.998 [1]
@rem %8 power factor: 0 = all frequencies change equally. [0]
@rem
@rem %9 image to be displaced
@rem
@rem Also uses:
@rem
@rem   fnA_SUPER_SAMP percentage for supersampling, eg 400. If blank or 100, no supersamplimg.
@rem   fnA_DISPLACE_ARGS
@rem   fnA_SINCOS makes fractal grayscale, and applies sin N to R and cos N to G.
@rem
@rem Updated:
@rem   24-September-2022 for IM v7.
@rem


@rem No compulsory parameters
@rem if "%1"=="" findstr /B "rem @rem" %~f0 & exit /B 1

@setlocal enabledelayedexpansion

@call echoOffSave

call %PICTBAT%setInOut %1 fa2


set NUM_FR=%1
if "%NUM_FR%"=="." set NUM_FR=
if "%NUM_FR%"=="" set NUM_FR=5

set FN_PARAM=%~2
if "%FN_PARAM%"=="." set FN_PARAM=
if "%FN_PARAM%"=="" set FN_PARAM=. . . .

set PREF=%3
if "%PREF%"=="." set PREF=
if "%PREF%"=="" set PREF=fna_fire_

set fnA_COL=%4
if "%fnA_COL%"=="." set fnA_COL=
if "%fnA_COL%"=="" set fnA_COL=0

set fnA_DX=%5
if "%fnA_DX%"=="." set fnA_DX=
if "%fnA_DX%"=="" set fnA_DX=0

set fnA_DY=%6
if "%fnA_DY%"=="." set fnA_DY=
if "%fnA_DY%"=="" set fnA_DY=0

set ZM=%7
if "%ZM%"=="." set ZM=
if "%ZM%"=="." set ZM=1

set fnA_POW_FAC=%8
if "%fnA_POW_FAC%"=="." set fnA_POW_FAC=
if "%fnA_POW_FAC%"=="" set fnA_POW_FAC=0

set SRC=%9
if "%SRC%"=="" set SRC=fn_fire.png

set WW=
for /F "usebackq" %%L in (`%IMG7%magick identify -format "WW=%%w\nHH=%%h" %SRC%`) do set %%L
if "%WW%"=="" (
  echo %0: can't find [%SRC%]
  exit /B 1
)


if "%fnA_SUPER_SAMP%"=="" set fnA_SUPER_SAMP=100

if "%fnA_SUPER_SAMP%"=="100" (
  set SS_UP=
  set SS_DN=
) else (
  set SS_UP=-resize %fnA_SUPER_SAMP%%%
  set SS_DN=-resize something
)

set SRC_SSUP=%PREF%_ssup.miff

%IMG7%magick ^
  %SRC% ^
  %SS_UP% ^
  %SRC_SSUP%

if "%fnA_DISPLACE_ARGS%"=="" set fnA_DISPLACE_ARGS=10%%%%x50%%%%

if "%fnA_SINCOS%"=="" (
  set sSINCOS=
) else (
  set sSINCOS=-channel R -evaluate sin %fnA_SINCOS% -channel G -evaluate cos %fnA_SINCOS%
)

set fnAUTO_LEVEL_EACH=0
set fnAUTO_LEVEL_END=0

set FRAME_EXT=.miff

set /A LAST_FR=%NUM_FR%-1

del %PREF%_fr??????%FRAME_EXT% 2>nul

set fnA_FRM_NUM=0
call %PICTBAT%fnInit %WW% %HH% %FN_PARAM%
  if ERRORLEVEL 1 (
    echo %0: fnInit failure
    exit /B 1
  )

for /L %%I in (0,1,%LAST_FR%) do (

  set LZ=000000%%I
  set LZ=!LZ:~-6!

  set fnA_FRM_NUM=%%I

  call %PICTBAT%fnOne ^
    %PREF%_fr!LZ!%FRAME_EXT%

  if ERRORLEVEL 1 (
    echo %0: fnOne failure
    exit /B 1
  )

  if not "%afSUPER_SAMP%"=="100" (
    set SS_DN=-resize "!fnWW!x!fnHH!^!"
  )

  %IMG7%magick ^
    %SRC_SSUP% ^
    ^( %PREF%_fr!LZ!%FRAME_EXT% ^
      %SS_UP% ^
      -auto-gamma ^
      -sigmoidal-contrast 10x50%% ^
      %sSINCOS% ^
    ^) ^
    -compose Displace ^
      -set option:compose:args %fnA_DISPLACE_ARGS% ^
      -composite ^
    !SS_DN! ^
    %PREF%_fr!LZ!%FRAME_EXT%

  if ERRORLEVEL 1 (
    echo %0: magick failure for SRC_SSUP [%SRC_SSUP%]
    exit /B 1
  )

  echo %~n0: %fnOUTFILE%
)

call %PICTBAT%frMinMax %PREF%_fr??????%FRAME_EXT%
if ERRORLEVEL 1 exit /B 1

call %PICTBAT%frGamma %PREF%_fr??????%FRAME_EXT% . 1
if ERRORLEVEL 1 exit /B 1

%IMG7%magick ^
  -loop 0 -delay 20 ^
  %PREF%_fr*%FRAME_EXT% ^
  -level %fmmMIN_PC%%%,%fmmMAX_PC%%% ^
  %PREF%.gif


call echoRestore

@endlocal & set faOUTFILE=%OUTFILE%

All images on this page were created by the commands shown, using:

%IMG7%magick -version
Version: ImageMagick 7.1.0-47 Q16-HDRI x64 15861e0:20220827 https://imagemagick.org
Copyright: (C) 1999 ImageMagick Studio LLC
License: https://imagemagick.org/script/license.php
Features: Cipher DPC HDRI OpenCL 
Delegates (built-in): bzlib cairo freetype gslib heic jng jp2 jpeg jxl lcms lqr lzma openexr pangocairo png ps raqm raw rsvg tiff webp xml zip zlib
Compiler: Visual Studio 2022 (193331629)

To improve internet download speeds, some images may have been automatically converted (by ImageMagick, of course) from PNG or TIFF or MIFF to JPG.

Source file for this web page is fracnseanim.h1. To re-create this web page, run "procH1 fracnseanim".


This page, including the images, is my copyright. Anyone is permitted to use or adapt any of the code, scripts or images for any purpose, including commercial use.

Anyone is permitted to re-publish this page, but only for non-commercial use.

Anyone is permitted to link to this page, including for commercial use.


Page version v1.0 12-September-2015.

Page created 25-Sep-2022 16:21:03.

Copyright © 2022 Alan Gibson.