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 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.
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 |
|
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.
|
|
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. |
|
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.
|
|
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. |
|
Blink comparator between
%IMG7%magick ^ -loop 0 -delay 50 ^ fna_exfr0rev.png ^ fna_exfr50.png ^ fna_exfr0r50.gif The frames are similar, but clearly different. |
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;
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.
|
|
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.
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 |
|
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,
|
|
Displace horizontally 3 pixels/frame. call %PICTBAT%fnAnim ^ . . fna_a_3 . 3 |
|
Displace vertically 3 pixels/frame. call %PICTBAT%fnAnim ^ . . fna_a_4 . . 3 |
|
Displace horizontally,
call %PICTBAT%fnAnim ^ . . fna_a_5 . -3 . . -1 |
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.
For animated zoom-out, integer power factors will create repeated patterns.
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.
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 |
|
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 |
|
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 |
|
Change the colour by 5% per frame;
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= |
|
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?
|
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
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= |
|
Use different parameters,
call %PICTBAT%fnAnim2 ^ 10 . fna_a_fire_2 3 . 5 . -0.5 ^ fn_fire_base.png |
|
Instead of displacing a simple gradient image,
call %PICTBAT%fnAnim2 ^ 10 . fna_a_fire_3 3 . 5 . -0.5 ^ fn_fire.png This creates many isolated pixels.
|
|
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= |
|
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= |
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.
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 |
|
call %PICTBAT%frTrim %PREF%_b_*%FRAME_EXT% 1 %IMG7%magick ^ -loop 0 -delay 20 ^ %PREF%_b_*%FRAME_EXT% ^ %PREF%_b.gif |
|
call %PICTBAT%frTrim %PREF%_c_*%FRAME_EXT% 1 %IMG7%magick ^ -loop 0 -delay 20 ^ %PREF%_c_*%FRAME_EXT% ^ %PREF%_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= |
Blah.
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?
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.
Distort time?
Blah.
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%.
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.
del fna_*.miff del fna_*0000*.png call %PICTBAT%fn_zeroEnvVar
For convenience, .bat scripts are also available in a single zip file. See Zipped BAT files.
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%
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%
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%
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%
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.