﻿

# Fractal noise

Fractal or Perlin noise is coherent and self-similar at different scales.

"Coherency" means that nearby pixels have similar values. "Self-similarity" means the image looks similar at different scales.

A different method for a similar effect would be to create an image entirely of noise, blur this a few times, then add the results. This would be computationally expensive.

This implementation uses Laplacian pyramids, as shown in Multi-scale pyramids.

One example on this page uses process modules. See my Process Modules page.

## The method

The script fractNoise.bat constructs and executes a convert command. This creates a number of images that each represent noise at different frequencies, then adds them together.

This creates and collapses a Laplacian pyramid (see Multi-scale pyramids) within a single convert. When used for individual images, the pyramid is not usually saved, though it can be if desired. When the fractal noise is animated, (see Fractal noise animations) the pyramid is created and saved at frame zero, and re-read for each other frame.

Each image is sometimes called an octave, because the noise frequency often doubles between them. In this implementation, any frequency multiplier greater than one may be used.

At each octave, noise is generated not for each separate pixel but instead in a grid pattern, and values at each pixel are interpolated from these.

To generate the final image, the corresponding pixels from all the enlarged grids are added, with appropriate weighting.

For example, one octave for a 600x400 pixel image might have 24x16 grid points. Each block is therefore 600/24 = 400/16 = 25 pixels square. We will magnify the noise grid by a factor of 25. With ImageMagick, we create a small noise grid with the required aspect ratio, and resize it to exactly the required dimensions.

In this implementation, blocks are always square, so grid points are equally spaced in the full-size image (which need not be square) in both the x- and y-directions.

 ```%IM%convert ^ -size 24x16 xc:rgb(50%%,50%%,50%%) ^ +noise Gaussian ^ +write fn_m1.png ^ -resize "600x400^!" ^ -auto-level ^ fn_m2.png```

This process is repeated for noise grids of 600x400, 300x200, ... perhaps down to 3x2 or even 1x1. As each resized noise image is generated, it is multiplied by an amplitude factor, and added to the previous result. This gives a weighted average. There are two purposes to the weighting:

1. It keeps intermediate and final results in the range 0 to 100%.
2. It applies a weighting that is derived from the frequency.

For these fractal noise Laplacian pyramids, weighting is scaled so the sum of the weights is 1.0, ie 100%. This is different to those on Multi-scale pyramids, where scaling makes the maximum 1.0, ie 100%. Pixel values are offset, so values of 50% represent zero. Hence, multiplication by a weight is performed by +level, not -evaluate Multiply.

Some implementations have a feature of recognising when an octave's amplitude insignificant, so they save time by not processing that octave. This implementation currently doesn't have that feature.

Above, we interpolate with "-resize". An alternative method uses "+distort SRT", which respects the "-virtual-pixel" setting.

 ```%IM%convert ^ fn_m1.png ^ -virtual-pixel Tile ^ +distort SRT "25,0" ^ -auto-level ^ fn_m3.png```

The result fn_m3.png is slightly different to fn_m3.png, mostly around the edges. Importantly, it is now tilable, as we can see:

```%IM%convert ^
fn_m3.png ^
( +clone ) +append +repage ^
( +clone ) -append +repage ^
fn_m4.png```

In general, the magnification may not be an integer, and may be different in the x and y directions.

 ```%IM%convert ^ fn_m1.png ^ -virtual-pixel Tile ^ +distort SRT "0,0,25,25,10,20,10" ^ +repage ^ -crop 600x400+0+0 +repage ^ -auto-level ^ fn_m5.png```

Each octave resembles blurred noise. Fractal noise where the block size is restricted to a narrow band closely resembles blurred noise.

This implementation should be usable with any Q-number ImageMagick, either integer or HDRI. When used to create displacement maps, higher Q-numbers (such as 32) may be beneficial. See Straightening horizons: How much precision? where I conclude that: "for images of a few thousand pixels in each dimension, displacement maps should be created and used with Q32 or better".

By default, the script uses ImageMagick programs found at %IM%. This can be overridden by setting %fnIM% to a different location.

## Script controls

The main controls are specified by giving parameters to the script. Each may be left to default by giving a single dot "." in its place.

 %1 Required image width, pixels. Default: 600 %2 Required image height, pixels. Default: 400 %3 The minimum block size. Small blocks give high-frequency noise. Default: 1 %4 The maximum block size. Large blocks give low-frequency noise. Default: min(height,width)/2 %5 The block factor. Should be >=1. Default: 2 %6 The power factor. Default: 0 %7 Output file. Default: fn.png

Parameters %3, %4, %5 and %6 correspond exactly to the same parameters of Multi-scale pyramids.

The script starts at block size %3 x %3. This is the smallest block size, which creates the highest frequency noise. Successive blocks are %5 times as large, at increasing sizes so at lower frequencies. It stops when the block size would be greater than or equal to %4.

Block sizes correspond to wavelengths, the inverse of frequencies.

Amplitudes are normalised so they sum to 1.0. Each is then multiplied by %fnCONST_FAC%, which defaults to 1.0. If %fnCONST_FAC% is set to a value greater then 1.0, the result is likely to be clipped at 100%. If a lower value is used, auto-levelling should usually be turned off.

See below for Examples of script arguments.

In addition, there are a large number of optional environment variables, all starting with "fn". Variables currently available are: fnSEED fnGRAY fnNOISE_TYPE fnABS_NSE fnFILTER fnIM fnPREFIX fnNG_EXT fnLINEAR fnAUTO_LEVEL_EACH fnAUTO_LEVEL_END fnAUTO_GAM_END fnRD_PYR fnWR_PYR fnPROC_EACH fnTXT_OUT fnA_FRM_NUM fnA_COL fnA_DX fnA_DY fnA_ZM fnA_POW_FAC fnA_COLCYCMETH . See the script, and text on this page. Variables that start with "fnA_" are for animation; see the page Fractal noise animations.

## Text output

If desired, the script will echo some text. This includes some statistics about each octave, including the minimum and maximum values generated. See the script for details. If text is wanted, set fnTXT_OUT=1.

```call %PICTBAT%fn_zeroEnvVar
set fnPREFIX=fn_txtout_
set fnTXT_OUT=1
set fnWR_PYR=1

call %PICTBAT%fractNoise ^
. . . . . . fn_examp.png >fn_fractnoise.lis

set fnWR_PYR=
set fnTXT_OUT=
set fnPREFIX=```

Here is fn_fractnoise.lis, the text output from the fractNoise.bat script. Lines beginning "levels" are written by the IM convert command.

``` f:\prose\PICTURES>rem Make fractal noise.
F:\pictures\fnInit: 600 400 1 300 2 0
levels 0: 0.444449530785077 0.555550469214923
levels 1: 0.444449530785077 0.555550469214923
levels 1: 0.444449530785077 0.555550469214923
levels 2: 0.395666437781338 0.604486152437629
levels 2: 0.444449530785077 0.555550469214923
levels 3: 0.359960326543069 0.650019073777371
levels 3: 0.445380331120775 0.554055085069047
levels 4: 0.325383382925155 0.67348744945449
levels 4: 0.444449530785077 0.552758068207828
levels 5: 0.300144960708019 0.700724803540093
levels 5: 0.44766918440528 0.555550469214923
levels 6: 0.291584649423972 0.711573968108644
levels 6: 0.444449530785077 0.544899671931029
levels 7: 0.271580071717403 0.733791103990234
levels 7: 0.450217441062028 0.543297474631876
levels 8: 0.255771725032425 0.73833829251545
levels 8: 0.469535362783246 0.507972838941024
levels before AL: 0.259098191805905 0.740062561989776
Created fn_examp.png
... and fn_txtout_fnpyr.tiff```

Here is fn_txtout_fn.bat, the IM convert command created and run by the fractNoise.bat script.

```c:\im\ImageMagick-6.9.9-50-Q16\convert ^
-compose Plus -virtual-pixel Tile -precision 15 ^
@fn_txtout_fn.scr ^
-format "levels before AL: %%[fx:minima] %%[fx:maxima]\n" +write info: ^
-auto-level -auto-gamma ^
+depth ^
+write fn_examp.png ^
+delete ^
mpr:GRD0 ^
mpr:GRD1 ^
mpr:GRD2 ^
mpr:GRD3 ^
mpr:GRD4 ^
mpr:GRD5 ^
mpr:GRD6 ^
mpr:GRD7 ^
mpr:GRD8 ^
+write fn_txtout_fnpyr.tiff ^
NULL:
if ERRORLEVEL 1 exit /B 1 ```

Here is fn_txtout_fn.scr, the script that is run within the convert command.

```( -size 600x400 xc:rgb(50%,50%,50%) +noise Random   +write mpr:GRD0   -virtual-pixel tile -distort SRT 0,0,1,0,0,0  -resize "600x400^!"     +level 44.4444444444445%,55.5555555555556% )
-format "levels 0: %[fx:minima] %[fx:maxima]\n" +write info:
( -size 300x200 xc:rgb(50%,50%,50%) +noise Random   +write mpr:GRD1   -virtual-pixel tile -distort SRT 0,0,1,0,0,0  -resize "600x400^!"     +level 44.4444444444445%,55.5555555555556% )
-format "levels 1: %[fx:minima] %[fx:maxima]\n" +write info:
-compose Mathematics -define compose:args=0,1,1,-0.5 -composite
( -size 150x100 xc:rgb(50%,50%,50%) +noise Random   +write mpr:GRD2   -virtual-pixel tile -distort SRT 0,0,1,0,0,0  -resize "600x400^!"     +level 44.4444444444445%,55.5555555555556% )
-format "levels 2: %[fx:minima] %[fx:maxima]\n" +write info:
-compose Mathematics -define compose:args=0,1,1,-0.5 -composite
( -size 75x50 xc:rgb(50%,50%,50%) +noise Random   +write mpr:GRD3   -virtual-pixel tile -distort SRT 0,0,1,0,0,0  -resize "600x400^!"     +level 44.4444444444445%,55.5555555555556% )
-format "levels 3: %[fx:minima] %[fx:maxima]\n" +write info:
-compose Mathematics -define compose:args=0,1,1,-0.5 -composite
( -size 38x25 xc:rgb(50%,50%,50%) +noise Random   +write mpr:GRD4   -virtual-pixel tile -distort SRT 0,0,1,0,0,0  -resize "600x400^!"     +level 44.4444444444445%,55.5555555555556% )
-format "levels 4: %[fx:minima] %[fx:maxima]\n" +write info:
-compose Mathematics -define compose:args=0,1,1,-0.5 -composite
( -size 19x13 xc:rgb(50%,50%,50%) +noise Random   +write mpr:GRD5   -virtual-pixel tile -distort SRT 0,0,1,0,0,0  -resize "600x400^!"     +level 44.4444444444445%,55.5555555555556% )
-format "levels 5: %[fx:minima] %[fx:maxima]\n" +write info:
-compose Mathematics -define compose:args=0,1,1,-0.5 -composite
( -size 9x6 xc:rgb(50%,50%,50%) +noise Random   +write mpr:GRD6   -virtual-pixel tile -distort SRT 0,0,1,0,0,0  -resize "600x400^!"     +level 44.4444444444445%,55.5555555555556% )
-format "levels 6: %[fx:minima] %[fx:maxima]\n" +write info:
-compose Mathematics -define compose:args=0,1,1,-0.5 -composite
( -size 5x3 xc:rgb(50%,50%,50%) +noise Random   +write mpr:GRD7   -virtual-pixel tile -distort SRT 0,0,1,0,0,0  -resize "600x400^!"     +level 44.4444444444445%,55.5555555555556% )
-format "levels 7: %[fx:minima] %[fx:maxima]\n" +write info:
-compose Mathematics -define compose:args=0,1,1,-0.5 -composite
( -size 2x2 xc:rgb(50%,50%,50%) +noise Random   +write mpr:GRD8   -virtual-pixel tile -distort SRT 0,0,1,0,0,0  -resize "600x400^!"     +level 44.4444444444445%,55.5555555555556% )
-format "levels 8: %[fx:minima] %[fx:maxima]\n" +write info:
-compose Mathematics -define compose:args=0,1,1,-0.5 -composite ```

## Grid images

The previous section, Text output, created a pyramid of noise grid images. We can extract each grid and show it with a version enlarged to the final image size.

Octave Noise grid Enlarged noise grid
8
7
6
5
4
3
2
1
0

The image that was created from these noise grids is:

 fn_examp.png

## Examples of script arguments

The following shows examples of the six basic parameters, listed above in Script controls.

Ensure fractNoise environment variable are not set:

`call %PICTBAT%fn_zeroEnvVar`

### Default parameters

Create a different example each time:

`set fnSEED=`
 ```call %PICTBAT%fractNoise ^ . . . . . . fn_1.png``` ```call %PICTBAT%fractNoise ^ . . . . . . fn_2.png``` ```call %PICTBAT%fractNoise ^ . . . . . . fn_3.png```

All following examples use the same seed.

`set fnSEED=1234`

### Vary image size

 ```call %PICTBAT%fractNoise ^ 75 50 . . . . fn_sz1.png``` ```call %PICTBAT%fractNoise ^ 150 100 . . . . fn_sz2.png``` ```call %PICTBAT%fractNoise ^ 300 200 . . . . fn_sz3.png``` ```call %PICTBAT%fractNoise ^ 600 400 . . . . fn_sz4.png``` ```call %PICTBAT%fractNoise ^ 75 50 . . . 1 fn_sz1w.png``` ```call %PICTBAT%fractNoise ^ 150 100 . . . 1 fn_sz2w.png``` ```call %PICTBAT%fractNoise ^ 300 200 . . . 1 fn_sz3w.png``` ```call %PICTBAT%fractNoise ^ 600 400 . . . 1 fn_sz4w.png```
```%IM%convert ^
fn_sz4.png -resize "75x50^!" ^
fn_sz1.png ^
-metric RMSE -compare -format %%[distortion] ^
info: ```
`0.0449111`
```%IM%convert ^
fn_sz4w.png -resize "75x50^!" ^
fn_sz1w.png ^
-metric RMSE -compare -format %%[distortion] ^
info: ```
`0.00315007`

Provided the same seed is used, images made at different sizes are remarkably similar.

### Vary minimum and maximum block sizes

 ```call %PICTBAT%fractNoise ^ . . . . . . fn_s1.png``` Omit highest frequencies. ```call %PICTBAT%fractNoise ^ . . 10 . . . fn_s2.png``` Omit lowest frequencies. ```call %PICTBAT%fractNoise ^ . . . 64 . . fn_s3.png``` Blocks at only one size. ```call %PICTBAT%fractNoise ^ . . 64 64 . . fn_s4.png```

### Vary block factor

 Small factors give a large variety of noise frequencies. ```call %PICTBAT%fractNoise ^ . . . . 1.1 . fn_bf1.png echo %fnNUM_OCTAVES% ``` `60 ` With so much added noise, the result is muddy. As previous, with auto-levelling. ```set fnAUTO_LEVEL_EACH=2 call %PICTBAT%fractNoise ^ . . . . 1.1 . fn_bf1a.png set fnAUTO_LEVEL_EACH=``` As previous, also auto-levelling at end. ```set fnAUTO_LEVEL_EACH=2 set fnAUTO_LEVEL_END=2 call %PICTBAT%fractNoise ^ . . . . 1.1 . fn_bf1b.png set fnAUTO_LEVEL_END= set fnAUTO_LEVEL_EACH=``` Large factors give little variety of noise frequencies. Here, just at 1, 10 and 100 pixels. ```call %PICTBAT%fractNoise ^ . . . . 10 . fn_bf2.png``` Perhaps a mottled-glass effect.

### Vary power factor

 Positive power factors emphasise low-frequency noise. ```call %PICTBAT%fractNoise ^ . . . . . 2 fn_pf1.png``` "1.5". ```call %PICTBAT%fractNoise ^ . . . . . 1.5 fn_pf2.png``` "1" gives the classic halving of amplitude per doubling of frequency. ```call %PICTBAT%fractNoise ^ . . . . . 1 fn_pf3.png``` "0.5". ```call %PICTBAT%fractNoise ^ . . . . . 0.5 fn_pf4.png``` "0" gives equal weight to all frequencies. ```call %PICTBAT%fractNoise ^ . . . . . 0 fn_pf5.png``` "-0.5". Negative factors emphasise high-frequency noise. ```call %PICTBAT%fractNoise ^ . . . . . -0.5 fn_pf6.png``` "-1" doubles the amplitude with each doubling of frequency. ```call %PICTBAT%fractNoise ^ . . . . . -1 fn_pf7.png``` Low-frequency noise is almost swamped.

## Parameters via environment variables

Many parameters are set by environment variables. Some are illustrated here.

### Auto-level

 ```call %PICTBAT%fn_zeroEnvVar set fnSEED=1234 set fnAUTO_LEVEL_EACH=0 call %PICTBAT%fractNoise ^ . . . . . . fn_al1.png``` ```set fnAUTO_LEVEL_EACH=1 call %PICTBAT%fractNoise ^ . . . . . . fn_al2.png``` ```set fnAUTO_LEVEL_EACH=2 call %PICTBAT%fractNoise ^ . . . . . . fn_al3.png set fnAUTO_LEVEL_EACH=```

With the default parameters, auto-levelling each octave makes very little difference (about 1% RMSE). For some settings, it can make a larger difference.

When used with animations, auto-levelling can result in flicker, so should generally not be used.

### Vary noise type

When used for animation that includes changing colours, noise types other than "Random" (the default) are not recommended. Other distributions can be created by using fnPROC_EACH.

 ```set fnNOISE_TYPE=Gaussian call %PICTBAT%fractNoise ^ . . . . . . fn_nt1.png``` ```set fnNOISE_TYPE=Gaussian set fnABS_NSE=1 call %PICTBAT%fractNoise ^ . . . . . . fn_nt1a.png set fnABS_NSE=``` ```set fnNOISE_TYPE=Gaussian set fnABS_NSE=1 call %PICTBAT%fractNoise ^ . . . . . 1 fn_nt1b.png set fnABS_NSE=``` Oil in water? Corny science-fiction movie? ```set fnNOISE_TYPE=Poisson call %PICTBAT%fractNoise ^ . . . . . . fn_nt2.png``` ```set fnNOISE_TYPE=Uniform call %PICTBAT%fractNoise ^ . . . . . . fn_nt3.png set fnNOISE_TYPE=``` ```set fnNOISE_TYPE=Impulse call %PICTBAT%fractNoise ^ . . . . . . fn_nt4.png set fnNOISE_TYPE=```

"+noise Impulse" makes an image mostly 50%, with a few pixels at 0 or 100%. fractNoise.bat transforms values so that 50% values become 100%, and both 0 and 100% become 0. Hence, this result is very light.

Similarly, "+noise Gaussian" creates most values around 50%, and these are transformed to be just under 100%. This is also true of other noise types. See the page on Noise.

Why does fractNoise.bat do this transformation? So that pixel colours can be animated while retaining fractal properties, and values after modulus addition such as 99.99% and 0.01% are very similar visually.

This transformation is also done for stills, so we can pick a still we like, and use the same parameters for animation.

Sadly, the transformation is not reversible.

### Vary filter

 ```set fnFILTER=Box call %PICTBAT%fractNoise ^ . . . . . . fn_f1.png``` ```set fnFILTER=Box call %PICTBAT%fractNoise ^ . . . . . 1 fn_f2.png``` ```set fnFILTER=Point call %PICTBAT%fractNoise ^ . . . . . . fn_f3.png``` Point is identical to Box. ```set fnFILTER=Cubic call %PICTBAT%fractNoise ^ . . . . . . fn_f4.png``` ```set fnFILTER=Triangle call %PICTBAT%fractNoise ^ . . . . . . fn_f5.png set fnFILTER=```

### Grayscale

When a grayscale image is wanted, the script takes the value from the red channel.

 ```set fnGRAY=1 call %PICTBAT%fractNoise ^ . . . . . . fn_g1.png``` ```call %PICTBAT%fractNoise ^ . . 10 40 1.1 . fn_g2.png set fnGRAY=```

### Colour cycling method

For static images, there is no colour cycling. However, the variable fnA_COLCYCMETH may have an effect on the output.

(For detail of the methods, see Colour cycling.)

When fnA_COLCYCMETH has no value, it takes a default value, which is:

• null if fnA_FRM_NUM is not set;
• sinarc2o if fnA_FRM_NUM is set.
 ```set fnA_COLCYCMETH=null call %PICTBAT%fractNoise ^ . . . . . . fn_cm1.png``` ```set fnA_COLCYCMETH=sinarc2o call %PICTBAT%fractNoise ^ . . . . . . fn_cm2.png %IM%compare -metric RMSE ^ fn_cm1.png fn_cm2.png NULL: ``` `1.41127 (2.15345e-005)` ```set fnA_COLCYCMETH=amsol call %PICTBAT%fractNoise ^ . . . . . . fn_cm3.png %IM%compare -metric RMSE ^ fn_cm1.png fn_cm3.png NULL: set fnA_COLCYCMETH=``` `9136.62 (0.139416)`

For a static image, results from null and sinarc2o are visually the same. But other methods such as amsol are very different.

### Linearize

A fractal noise image is a mathematical construct with the mean near 50%, written to an image file that IM declares to be in non-linear sRGB colorspace.

We can readily declare the result to be in linear RGB colorspace, and convert to sRGB so the mean is around 72%.

Or we can insert processing that linearizes the generated noise, and delinearizes the result.

 ```call %PICTBAT%fractNoise ^ . . . . . . fn_lin1.png %IM%convert ^ fn_lin1.png ^ -format "mean=%%[fx:mean]" ^ info: ``` `mean=0.506222` ```%IM%convert ^ fn_lin1.png ^ -format "mean=%%[fx:mean]" ^ -set colorspace RGB ^ -colorspace sRGB ^ +write info: ^ fn_lin2.png ``` `mean=0.736099` ```set fnLINEAR=1 call %PICTBAT%fractNoise ^ . . . . . . fn_lin3.png %IM%convert ^ fn_lin3.png ^ -format "mean=%%[fx:mean]" ^ info: set fnLINEAR=``` `mean=0.518904`

How much difference does fnLINEAR make?

```%IM%compare -metric RMSE ^
fn_lin1.png fn_lin3.png NULL: ```
`1894.91 (0.0289145)`

Despite the reasonably large difference, nearly 3%, I can't see any difference with a blink comparison. (Building a GIF blink-comparator doesn't work well here, as the quantisation introduces more differences.)

## Variations

By default, the largest block size is half the smaller of the width and height, so 200 in this case. We can make it as large as we like.

 ```call %PICTBAT%fn_zeroEnvVar set fnSEED=1234 call %PICTBAT%fractNoise ^ . . . 600 . . fn_lb1.png``` With large blocks, and power factor=1, low-frequency noise dominates. ```call %PICTBAT%fractNoise ^ . . . 600 . 1 fn_lb2.png``` Perhaps a similar texture to murky clouds. ```call %PICTBAT%fractNoise ^ . . 250 1000 . . fn_lb3.png``` ```set fnGRAY=1 call %PICTBAT%fractNoise ^ . . 1 10 . . fn_lb4.png set fnGRAY=``` Resembles magnified silver-halide film grain at a constant middle exposure.

We can create one-dimensional fractal noise images. As these do not display readily, we use graphLineCol.bat to make graphs.

 Output dimension 600x1. The maximum block size would default to 1, so must be set explicitly. ```set fnGRAY=1 call %PICTBAT%fractNoise ^ 600 1 1 300 . . fn_c1.png call %PICTBAT%graphLineCol ^ fn_c1.png set fnGRAY=``` ```call %PICTBAT%fractNoise ^ 600 1 1 300 . . fn_c2.png call %PICTBAT%graphLineCol ^ fn_c2.png``` Why is high-frequency amplitude less than in fn_c1? Because fn_c1 uses the values from the red channel. If fn_c2, these have been attenuated by the values in the other two channels. Reduce amplitude of low-frequency noise somewhat. ```call %PICTBAT%fractNoise ^ 600 1 1 300 . 0.5 fn_c2a.png call %PICTBAT%graphLineCol ^ fn_c2a.png``` Reduce amplitude of low-frequency noise more. ```call %PICTBAT%fractNoise ^ 600 1 1 300 . 1 fn_c2b.png call %PICTBAT%graphLineCol ^ fn_c2b.png``` These could be cross-sections of a mountain. ```call %PICTBAT%fractNoise ^ 600 1 4 300 . . fn_c3.png call %PICTBAT%graphLineCol ^ fn_c3.png``` Gaussian noise, without auto-level. ```set fnNOISE_TYPE=Gaussian set fnAUTO_LEVEL_EACH=0 set fnAUTO_LEVEL_END=0 call %PICTBAT%fractNoise ^ 600 1 4 300 . . fn_c4.png call %PICTBAT%graphLineCol ^ fn_c4.png ``` Again, but absolute. [blah doesn't work] ```set fnABS_NSE=1 call %PICTBAT%fractNoise ^ 600 1 4 300 . . fn_c5.png call %PICTBAT%graphLineCol ^ fn_c5.png``` Again, but absolute, and power factor 1. ```call %PICTBAT%fractNoise ^ 600 1 4 300 . 1 fn_c6.png call %PICTBAT%graphLineCol ^ fn_c6.png``` Impulse noise, absolute, without auto-level. ```set fnNOISE_TYPE=Impulse call %PICTBAT%fractNoise ^ 600 1 4 300 . . fn_c7.png call %PICTBAT%graphLineCol ^ fn_c7.png set fnABS_NSE= set fnAUTO_LEVEL_END= set fnAUTO_LEVEL_EACH= set fnNOISE_TYPE=```

## Processing each octave

Effects can be applied to each octave. Set fnPROC_EACH to the desired processing. The value of this environment variable will be inserted into the appropriate part of the script. It must be valid IM syntax. It will be given a list of exactly one image, the octave at the final size. It should leave one image at the same size.

```call %PICTBAT%fn_zeroEnvVar
set fnSEED=1234```
 ```set fnPROC_EACH=^^^( +clone ^ -colorspace Gray ^ -auto-level -auto-gamma ^ -shade 135x30 ^ -auto-level ^^^) ^ -compose Hard_Light -composite ^ -auto-level -auto-gamma call %PICTBAT%fractNoise ^ . . . . . . fn_po1.png``` The same "-shade" process, grey, slightly weighting low frequencies. ```set fnGRAY=1 call %PICTBAT%fractNoise ^ . . 3 64 . 0.5 fn_po2.png set fnGRAY=``` Perhaps weathered stone. For comparison, as previous but with no processing. ```set fnPROC_EACH= set fnGRAY=1 call %PICTBAT%fractNoise ^ . . 3 64 . 0.5 fn_po3.png set fnGRAY=``` ```set fnPROC_EACH=-evaluate Pow 2 call %PICTBAT%fractNoise ^ . . . . . . fn_po4.png``` ```set fnPROC_EACH=-threshold 50%%%% call %PICTBAT%fractNoise ^ . . . . . . fn_po5.png``` ```set fnPROC_EACH=-channel RGB -threshold 50%%%% +channel call %PICTBAT%fractNoise ^ . . . . . . fn_po6.png``` This uses process modules, so we set fnIM to an IM directory that includes them. ```set fnPROC_EACH=^^^( -size 1x10000 gradient: -rotate 90 ^ -function Polynomial -1,2,0 -evaluate Pow 0.5 ^ -process 'cumulhisto norm' ^ -process 'mkhisto cumul norm' ^ ^^^) ^ -clut set fnIM=%IMDEV% call %PICTBAT%fractNoise ^ . . . . . . fn_po7.png set fnIM= set fnPROC_EACH=```

Aesthetically, shaded images may be improved by increasing mid-tone contrast:

 ```%IM%convert ^ fn_po1.png ^ -sigmoidal-contrast 5x50%% ^ fn_po1a.png``` ```%IM%convert ^ fn_po2.png ^ -sigmoidal-contrast 5x50%% ^ fn_po2a.png```

## Post-processing

After a fractal noise image has been generated, it can be processed in many ways.

 ```call %PICTBAT%fn_zeroEnvVar set fnSEED=1234 call %PICTBAT%fractNoise ^ 600 1200 . . . . fn_v1.png %IM%convert ^ fn_v1.png ^ -resize "600x400^!" ^ fn_v1.png``` ```call %PICTBAT%fractNoise ^ 1800 400 . . . . fn_v1a.png %IM%convert ^ fn_v1a.png ^ -resize "600x400^!" ^ fn_v1a.png``` ```call %PICTBAT%fractNoise ^ 1200 800 . . . . fn_v1b.png set PERSP=0,0 300,0 ^ 1199,0 899,0 ^ 0,799 0,399 ^ 1199,799 1199,399 %IM%convert ^ fn_v1b.png ^ -distort Perspective "%PERSP%" ^ -gravity North ^ -crop 600x400+0+0 +repage ^ fn_v1b.png``` Distort horizontally to an ellipse. For the fx, see Shape to shape. ```set fnGRAY= call %PICTBAT%fractNoise ^ . . . . . . fn_v1c.png %IM%convert ^ fn_v1c.png ^ -alpha Opaque ^ -virtual-pixel None ^ -fx ^ JH=(j^>h/2)?h-j-1:j;^ JHM=(h/2-JH)/h;^ F=(JH==0)?0:1/(sqrt(1-4*JHM*JHM));^ p{((i/w-0.5)*F+0.5)*w,j} ^ fn_v1ca.png``` Distort with the method from Polar distortions: planets. ```set PL_SC=0,0,#000,^ %%[fx:w-1],0,#f00,^ 0,%%[fx:h-1],#0f0,^ %%[fx:w-1],%%[fx:h-1],#ff0 %IM%convert ^ fn_v1c.png ^ -virtual-pixel None ^ -distort depolar 0 ^ ( +clone ^ -sparse-color bilinear "%PL_SC%" ^ -channel G ^ -function ArcSin 2,0,2,0 ^ +channel ^ ) ^ -compose Distort ^ -set option:compose:args 100%%x100%% ^ -composite ^ -distort polar 0 ^ fn_v1cb.png``` VP=none causes discontinuity. ```set fnGRAY=1 call %PICTBAT%fractNoise ^ . . 10 . . . fn_v2.png``` ```set fnGRAY=1 set fnNOISE_TYPE=Impulse set fnTILE=1 call %PICTBAT%fractNoise ^ . . . . . . fn_vpl2.png set fnTILE= set fnNOISE_TYPE=``` Polar (roll up) with VP=edge, then mask with a circle. ```%IM%convert ^ fn_vpl2.png ^ -distort depolar 0 ^ ( +clone ^ -sparse-color bilinear "%PL_SC%" ^ -channel G ^ -function ArcSin 2,0,2,0 ^ +channel ^ ) ^ -compose Distort ^ -set option:compose:args 100%%x100%% ^ -composite ^ ( -clone 0 ^ -virtual-pixel Edge ^ -distort polar 0 ^ ) ^ ( -clone 0 ^ -fill Black -colorize 100 ^ -fill White ^ -draw "circle 300,200 300,400" ^ ) ^ -delete 0 ^ -alpha off ^ -compose CopyOpacity -composite ^ fn_vpl2b.png``` ```%IM%convert ^ fn_v2.png ^ ( -size 1x500 gradient: -rotate 90 ^ -duplicate 19 +append +repage ) ^ -clut ^ -morphology edgein diamond:1 ^ -threshold 40%% ^ fn_v2a.png set fnGRAY=``` ```set fnGRAY=1 call %PICTBAT%fractNoise ^ . . . . . . fn_vg1.png``` Increase local contrast ```call %PICTBAT%exHvyBlr ^ fn_vg1.png . . fn_vg1b.png ``` Apply a simple clut ```%IM%convert ^ fn_vg1b.png ^ ( xc:#008 xc:#0f0 +append +repage ) ^ -clut ^ fn_vg1c.png``` Colouring for wood ```set fnGRAY=1 call %PICTBAT%fractNoise ^ 1200 2400 . . . 1 fn_w1.png %IM%convert ^ fn_w1.png ^ -resize "600x400^!" ^ ( xc:#321 xc:#531 xc:#841 xc:#a52 +append +repage ) ^ -clut ^ fn_w1a.png set fnGRAY=``` The same fractal noise provides grain for wood ```%IM%convert ^ fn_w1.png ^ -resize "600x400^!" ^ ( -size 1x500 gradient: -rotate 90 ^ -evaluate sin 8 ^ -sigmoidal-contrast 5,10%% ^ +append +repage ) ^ -clut ^ fn_w1b.png``` Wood = Colouring * Grain ```%IM%convert ^ fn_w1a.png ^ fn_w1b.png ^ -compose Multiply -composite ^ fn_w1c.png``` Camouflage pattern. Integer interpolation. ```%IM%convert ^ fn_g2.png ^ -auto-level -auto-gamma ^ ( xc:#131 xc:#882 xc:#481 +append +repage ) ^ -interpolate Integer ^ -clut ^ fn_cam1.png``` Tweaked camouflage pattern. ```%IM%convert ^ fn_g2.png ^ -auto-level -auto-gamma ^ ( +clone ^ -sparse-color bilinear ^ "0,0,White 0,%%[fx:h-1],Black" ^ ) ^ -compose Hard_Light -composite ^ ( xc:#131 xc:#882 xc:#481 +append +repage ) ^ -interpolate Integer ^ -clut ^ fn_cam2.png``` Tweaked camouflage pattern, default interpolation. ```%IM%convert ^ fn_g2.png ^ -auto-level -auto-gamma ^ ( +clone ^ -sparse-color bilinear ^ "0,0,White 0,%%[fx:h-1],Black" ^ ) ^ -compose Hard_Light -composite ^ ( xc:#131 xc:#882 xc:#481 +append +repage ) ^ -clut ^ fn_cam3.png```

Film grain can be applied with "-compose Hard_Light".

 When the grain image is sufficiently large: ```%IM%convert ^ toes.png ^ fn_lb4.png ^ -compose Hard_Light -composite ^ fn_grn1.png``` When the grain image is small, and needs to be tiled up: ```%IM%convert ^ fn_lb4.png -resize 10%% ^ +write mpr:TGRAIN +delete ^ toes.png ^ ( +clone ^ -tile mpr:TGRAIN -draw "color 0,0 reset" ^ ) ^ -compose Hard_Light -composite ^ fn_grn2.png```

## Tiling

To make fractal noise images that are tilable, set fnTILE to 1. This slows processing and may create a noticable edge effect, so I leave it off unless I really want it.

 ```call %PICTBAT%fn_zeroEnvVar set fnSEED=1234 set fnTILE=1 call %PICTBAT%fractNoise ^ 300 200 . . . . fn_t1.png %IM%convert ^ fn_t1.png ^ ( +clone ) +append +repage ^ ( +clone ) -append +repage ^ fn_t1t.png``` ```call %PICTBAT%fractNoise ^ 300 200 . 300 . 1 fn_t2.png %IM%convert ^ fn_t2.png ^ ( +clone ) +append +repage ^ ( +clone ) -append +repage ^ fn_t2t.png``` ```set fnGRAY=1 call %PICTBAT%fractNoise ^ 300 200 . . . . fn_t3.png %IM%convert ^ fn_t3.png ^ ( +clone ) +append +repage ^ ( +clone ) -append +repage ^ fn_t3t.png set fnGRAY= set fnTILE=```

Note: it appears that "-virtual-pixel Tile" needs to be repeated within each parenthesis.

A tilable image that is tiled an integer number of times results in another tilable image.

Tilable images can be rolled up. The following are made from 2x2 tiles, so they also have symmetry.

 ```%IM%convert ^ fn_t1t.png ^ -distort polar -1,0 ^ fn_t1tp1.png``` ```%IM%convert ^ fn_t1t.png ^ -distort polar 0,0 ^ fn_t1tp2.png``` ```%IM%convert ^ fn_t1t.png ^ -virtual-pixel None ^ -distort polar 0,0,300,200,-90,90 ^ -flip ^ -crop x50%%+0+0 +repage ^ fn_t1tp3.png``` ```%IM%convert ^ fn_t3t.png ^ -distort polar -1,0 ^ fn_t3tp1.png``` ```%IM%convert ^ fn_t3t.png ^ ( +clone ^ -sparse-color bilinear ^ "0,0,White 0,%%[fx:h-1],Black" ^ ) ^ -compose Hard_Light -composite ^ -distort polar -1,0 ^ -evaluate Pow 1.5 ^ fn_t3tp2.png``` ```%IM%convert ^ fn_t3tp2.png ^ -posterize 4 ^ fn_t3tp3.png``` ```call %PICTBAT%lgstConnComp ^ fn_t3tp3.png ^ fn_t3tp3c.png``` Level, and quarter-circle clut so we don't have pointed centre. ```%IM%convert ^ fn_t3tp2.png ^ -level 50%%,100%% ^ ( -size 1x100 gradient: -rotate 90 ^ -function Polynomial -1,2,0 ^ -evaluate Pow 0.5 ^ ) ^ -clut ^ fn_t3tp2b.png``` Add 20% of height at top, resize up vertically, displace vertically absolute, resize down vertically. pi/2 = 1.5708 = 1/0.6366 ```set SP=^ 0,0,#000,^ %%[fx:w-1],0,#f00,^ 0,%%[fx:h-1],#0f0,^ %%[fx:w-1],%%[fx:h-1],#ff0 %IM%convert ^ fn_t3tp2b.png ^ ( +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%%^!" ^ -trim +repage ^ fn_t3tp2c.png``` ```%IM%convert ^ fn_t3tp2c.png ^ -shade 135x30 ^ fn_t3tp2d.png```

Tiling can have unexpected side-effects:

 Without tiling. ```call %PICTBAT%fn_zeroEnvVar set fnSEED=1234 set fnFILTER=Box set fnTILE=0 call %PICTBAT%fractNoise ^ . . . . . 1 fn_to1.png``` With tiling. ```set fnTILE=1 call %PICTBAT%fractNoise ^ . . . . . 1 fn_to2.png set fnTILE=```

## Designing for animation

When creating a still image that will be animated, auto-levelling at each frame should be turned off, an any automatic change of tone for individual frames will generally result in flickering. The script exHvyBlr.bat may be useful for boosting local contrast.

As this image will be rolled up, we use fnTILE=1 to avoid disconinuity from the top-centre downwards.

The convert command processes the fractal noise image, following the steps shown above. It also saves intermediate images, which is helpful during development of the script.

• fn_cld_a.png is the fractal noise image, extrapolated from heavy blur to increase local contrast.
• fn_cld_b.png is the result after applying gradient lighting and depolar ("rolling up").
• fn_cld_c.png is the result after levelling and applying an extreme clut that massively increases contrast near black, and decreases contrast near white.
• fn_cld_d.png is the result after extending the top by 20% of the height, enlarging vertically, distorting vertically, shrinking vertically, and finally cropping.
 ```call %PICTBAT%fn_zeroEnvVarAnim set fnSEED=1234 set fnGRAY=1 set fnTILE=1 call %PICTBAT%fractNoise ^ 600 600 . . . . fn_cld_a.png call %PICTBAT%exHvyBlr ^ fn_cld_a.png . . fn_cld_a.png set SP=^ 0,0,#000,^ %%[fx:w-1],0,#f00,^ 0,%%[fx:h-1],#0f0,^ %%[fx:w-1],%%[fx:h-1],#ff0 %IM%convert ^ fn_cld_a.png ^ ^( +clone ^ -sparse-color bilinear ^ "0,0,White 0,%%[fx:h-1],Black" ^ ^) ^ -compose Hard_Light -composite ^ -distort polar -1,0 ^ -level 50%%,100%% ^ +write fn_cld_b.png ^ ^( -size 1x100 gradient: -rotate 90 ^ -evaluate Pow 0.5 ^ -function Polynomial -1,2,0 ^ -evaluate Pow 0.5 ^ ^) ^ -clut ^ +write fn_cld_c.png ^ ^( +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 ^ fn_cld_d.png set fnTILE= set fnA_COLCYCMETH= set fnGRAY= set fnAUTO_GAM_END= set fnAUTO_LEVEL_END=```

## Displacements

Fractal noise images can be used as (relative) displacement maps for other images, which might also be fractal noise images.

 ```set SRC1=fn_1.png set SRC2=fn_2.png set SRC3=fn_vg1.png %IM%convert ^ %SRC3% ^ -channel R -evaluate sin 1 ^ -channel G -evaluate cos 1 ^ +channel ^ fn_disp1.png``` ```%IM%convert ^ %SRC2% ^ %SRC1% ^ -compose Displace ^ -set option:compose:args 3%%x3%% ^ -composite ^ fn_disp2.png``` ```%IM%convert ^ %SRC2% ^ fn_disp1.png ^ -compose Displace ^ -set option:compose:args 3%%x3%% ^ -composite ^ fn_disp3.png``` ```%IM%convert ^ %SRC2% ^ ( fn_g1.png ^ -channel R -evaluate sin 1 ^ -channel G -evaluate cos 1 ^ +channel ^ ) ^ -compose Displace ^ -set option:compose:args 3%%x3%% ^ -composite ^ fn_disp4.png``` A "swirl" effect. As previous, but with 3 cycles of sin & cos. ```%IM%convert ^ %SRC2% ^ ( fn_g1.png ^ -channel R -evaluate sin 3 ^ -channel G -evaluate cos 3 ^ +channel ^ ) ^ -compose Displace ^ -set option:compose:args 3%%x3%% ^ -composite ^ fn_disp5.png``` Dispacing with narrow-band fractal noise. ```%IM%convert ^ %SRC2% ^ ( fn_g2.png ^ -channel R -evaluate sin 1 ^ -channel G -evaluate cos 1 ^ +channel ^ ) ^ -compose Displace ^ -set option:compose:args 3%%x3%% ^ -composite ^ fn_disp6.png``` As previous, but with 3 cycles of sin & cos. ```%IM%convert ^ %SRC2% ^ ( fn_g2.png ^ -channel R -evaluate sin 3 ^ -channel G -evaluate cos 3 ^ +channel ^ ) ^ -compose Displace ^ -set option:compose:args 3%%x3%% ^ -composite ^ fn_disp7.png``` As previous, but displacing a photograph. ```%IM%convert ^ toes.png ^ ( fn_g2.png ^ -channel R -evaluate sin 3 ^ -channel G -evaluate cos 3 ^ +channel ^ ) ^ -compose Displace ^ -set option:compose:args 3%%x3%% ^ -composite ^ fn_disp8.png``` This has introduced a false sharpness, which may or may nor be desirable. Supersampling is more accurate. ```%IM%convert ^ toes.png ^ -resize 400%% ^ ( fn_g2.png ^ -resize 400%% ^ -channel R -evaluate sin 3 ^ -channel G -evaluate cos 3 ^ +channel ^ ) ^ -compose Displace ^ -set option:compose:args 3%%x3%% ^ -composite ^ -resize 25%% ^ fn_disp9.png``` Fire. We make a base image, and do a relative displacement vertically. ```%IM%convert ^ xc:#000 xc:#840 xc:#ff8 xc:#ff0 ^ xc:#f80 xc:#f00 xc:#a00 xc:#800 ^ xc:#000 ^ -append +repage ^ -resize "600x400^!" ^ +write fn_fire_base.png ^ ( fn_g2.png ^ -auto-gamma ^ -sigmoidal-contrast 10x50%% ^ ) ^ -compose Displace ^ -set option:compose:args 0%%x50%% ^ -composite ^ fn_fire.png```

## Animation

The fractNoise.bat script contains features for animation. See Fractal noise animations for explanations and a variety of examples.

## Reverse fractal noise

The method shown on this page synthesises an image from noise grid images and weighting factors. It constructs a Laplacian pyramid of noise, then collapses the pyramid to create an image. Multi-scale pyramids showed a method for constructing a Laplacian pyramid from an image, then collapsing the pyramid (possibly modified) to re-form the image (or a variation).

From fractal noise image fn_examp.png made above in section Text output, we use a script from Multi-scale pyramids to construct a pyramid.

```call %PICTBAT%fn_zeroEnvVar
set fnSEED=1234

set mkpDEBUG=1
set mkpHTM=1
set pyPREFIX=fn_rfn_

set pyIM=%IMDEV%

call %PICTBAT%mkLapPyr fn_examp.png fn_rfn.tiff

set pyPREFIX=```

Here is the pyramid created by the script:

`%IM%identify fn_rfn.tiff `
```fn_rfn.tiff[0] TIFF 600x400 600x400+0+0 32-bit sRGB 3.15094MiB 0.000u 0:00.000
fn_rfn.tiff[1] TIFF 300x200 300x200+0+0 32-bit sRGB 3.15094MiB 0.000u 0:00.000
fn_rfn.tiff[2] TIFF 150x100 150x100+0+0 32-bit sRGB 3.15094MiB 0.000u 0:00.000
fn_rfn.tiff[3] TIFF 75x50 75x50+0+0 32-bit sRGB 3.15094MiB 0.000u 0:00.000
fn_rfn.tiff[4] TIFF 38x25 38x25+0+0 32-bit sRGB 3.15094MiB 0.000u 0:00.000
fn_rfn.tiff[5] TIFF 19x13 19x13+0+0 32-bit sRGB 3.15094MiB 0.000u 0:00.000
fn_rfn.tiff[6] TIFF 9x6 9x6+0+0 32-bit sRGB 3.15094MiB 0.000u 0:00.000
fn_rfn.tiff[7] TIFF 5x3 5x3+0+0 32-bit sRGB 3.15094MiB 0.000u 0:00.000
fn_rfn.tiff[8] TIFF 600x400 600x400+0+0 32-bit Grayscale Gray 3.15094MiB 0.000u 0:00.000```
Octave Grid Grid, resized up Histogram of resized grid Difference
7
6
5
4
3
2
1
0

The pyramid created by mkLapPyr.bat is clearly different to the more colourful pyramid that was used to create the image, as shown above in Grid images. The enlarged grids approach a Laplacian distribution, except for the last, which is more Gaussian. (Too my naked eye, the final enlarged grid appears a smooth gray. With a magnifying glass, I clearly see the noise.)

However, the final difference for this new pyramid is zero:

`%IM%identify -format "min=%%[fx:minima] max=%%[fx:maxima]" fn_rfn.tiff[%mklpNUM_OCTAVES%] `
`min=0.500008 max=0.500008`

And from this new pyramid file, we can attempt to reconstruct the original image. Then we compare the reconstruction with the original.

 ```%pyIM%convert ^ @fn_rfn_mklp_recon.scr ^ fn_rfn_recon.png %pyIM%compare ^ -metric RMSE ^ fn_examp.png ^ fn_rfn_recon.png ^ NULL: >fn_rfn_comp.lis 2^>^&1 ``` `0 (0)`

It is the same.

Fractal noise pyramids are built with the sum method of weighting. The script mkLapPyr.bat by default uses the max method of weighting, but we can tell it to use the sum method. However, this is less successful.

```set mkpDEBUG=1
set mkpHTM=1
set pyPREFIX=fn_rfn_sum_

set pyWEIGHTING=sum

call %PICTBAT%mkLapPyr fn_examp.png fn_rfn_sum.tiff

set pyWEIGHTING=

set pyPREFIX=```

Here is the pyramid created by the script:

Octave Grid Grid, resized up Histogram of resized grid Difference
7
6
5
4
3
2
1
0

This pyramid is less successful. Because the weighting is less. each grid doesn't reduce the difference by much, so that doesn't remove much data, so the next grid still retains low-frequency data. The final grid clearly contains both high and low frequency data. This more closely resembles a Gaussian pyramid.

The final difference for this new pyramid is not zero. Visually, it looks like the input image with reduced saturation.

`%IM%identify -format "min=%%[fx:minima] max=%%[fx:maxima]" fn_rfn_sum.tiff[%mklpNUM_OCTAVES%] `
`min=0.265156 max=0.770031`

We can attempt to reconstruct the original image. Then we compare the reconstruction with the original.

 ```%pyIM%convert ^ @fn_rfn_sum_mklp_recon.scr ^ fn_rfn_sum_recon.png %pyIM%compare ^ -metric RMSE ^ fn_examp.png ^ fn_rfn_sum_recon.png ^ NULL: >fn_rfn_sum_comp.lis 2^>^&1 ``` `0 (0)`

It is the same. The reconstruction does work, but only because we have used the final difference.

## Future

### Performance

On my laptop, the script fractNoise.bat takes about 1.6 seconds to make a 600x400 pixel image, of which 1.1s is in fnInit.bat. Of the 0.5s in fnOne.bat, 0.3s is in the convert command.

For a 7500x5000 image, it takes 33.5s, of which 31.5s is in the convert command.

Translating the script to C (or a faster script language) could result in a five-fold performance improvement for small images.

A sequence of animated fractal noise makes one call to fnInit.bat, then one call to fnOne.bat per frame. There is further scope for improving animations within a compiled program, as the noise pyramid files would not need to be re-read for every frame.

A process module could be written for IM, or code might be implemented as a separate command. However, fractNoise.bat accepts up to about 26 arguments, and implementing each as a "-define" would be messy.

Does round-trip create fractal noise image from pyramid, then pyramid from the image, work? blah.

### Weighting by clut

Currently, the amplitudes of octaves are weighted by their frequency, using a power factor. This can weight them all the same, or emphasise either the lower or the higher frequencies. It cannot, for example, weight the middle frequencies more than high and low frequencies. This feature may be desirable.

A possible mechanism is to use a clut, which could be Gaussian or any other shape. (See the Clut cookbook.) Then each frequency would use the appropriate look-up from the clut, perhaps using Process modules: interppix.

## Cleanup

```del fn_*0000*.png

call %PICTBAT%fn_zeroEnvVar

set fnWW=
set fnHH=```

## Scripts

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

### fn_zeroEnvVar.bat

This script removes environment variables that are used by fractNoise.bat. It is automatically generated from fractNoise.bat when this page is built.

```set fnSEED=
set fnGRAY=
set fnNOISE_TYPE=
set fnABS_NSE=
set fnFILTER=
set fnIM=
set fnPREFIX=
set fnNG_EXT=
set fnLINEAR=
set fnAUTO_LEVEL_EACH=
set fnAUTO_LEVEL_END=
set fnAUTO_GAM_END=
set fnRD_PYR=
set fnWR_PYR=
set fnPROC_EACH=
set fnTXT_OUT=
set fnA_FRM_NUM=
set fnA_COL=
set fnA_DX=
set fnA_DY=
set fnA_ZM=
set fnA_POW_FAC=
set fnA_COLCYCMETH=```

### fn_zeroEnvVarAnim.bat

```rem Set fractNoise environment variables for animation.

call %PICTBAT%fn_zeroEnvVar

set fnAUTO_LEVEL_EACH=0
set fnAUTO_LEVEL_END=0
set fnAUTO_GAM_END=0
set fnA_COLCYCMETH=sinarc2o```

### fractNoise.bat

This is a simple wrapper for the two scripts below, fnInit.bat and fnOne.bat, that do the real work.

```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 convert 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 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=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%```

### fnInit.bat

```rem Fractal noise initialisation: sets environment variables

@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 No compulsory parameters
@rem No setlocal

set fix=99
if not "!fix!"=="99" (
echo %0: must be run in enabledelayedexpansion
exit /B 1
)
set fix=

set fnWW=%1
if "%fnWW%"=="." set fnWW=
if "%fnWW%"=="" set fnWW=600
set fnHH=%2
if "%fnHH%"=="." set fnHH=
if "%fnHH%"=="" set fnHH=400

set BLK_MIN=%3
if "%BLK_MIN%"=="." set BLK_MIN=
if "%BLK_MIN%"=="" set BLK_MIN=1
set BLK_MAX=%4
if "%BLK_MAX%"=="." set BLK_MAX=
if "%BLK_MAX%"=="" (
for /F "usebackq" %%L in (`%IM%identify ^
-format "BLK_MAX=%%[fx:max(%fnWW%,%fnHH%)/2]" ^
xc:`) do set %%L
)

set BLK_FAC=%5
if "%BLK_FAC%"=="." set BLK_FAC=
if "%BLK_FAC%"=="" set BLK_FAC=2

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

echo %0: %fnWW% %fnHH% %BLK_MIN% %BLK_MAX% %BLK_FAC% %POW_FAC%

if "%fnIM%"=="" set fnIM=%IM%

if "%fnPREFIX%"=="" set fnPREFIX=fn_

if "%fnNG_EXT%"=="" set fnNG_EXT=.tiff

set pyIM=%fnIM%

set pyPREFIX=%fnPREFIX%
set pyLINEAR=%fnLINEAR%

set pyWEIGHTING=sum

set BLK_LIS=%fnPREFIX%blk.lis

set NUM_OCTAVES=

rem call %PICTBAT%mkpVar %fnWW% %fnHH% %3 %4 %5 %6
call %PICTBAT%mkpVar %fnWW% %fnHH% %BLK_MIN% %BLK_MAX% %BLK_FAC% %POW_FAC%
if ERRORLEVEL 1 exit /B 1

rem if not exist %BLK_LIS% (
rem   echo %0: Can't find BLK_LIS [%BLK_LIS%]
rem   exit /B 1
rem )
rem
rem
rem for /F "tokens=*" %%L in (%BLK_LIS%) do set "%%L"

if "%NUM_OCTAVES%"=="" (
echo %0: NUM_OCTAVES has no value
exit /B 1
)

if not "%fnA_FRM_NUM%"=="" call %PICTBAT%mkpAnimVar

if "%fnNOISE_TYPE%"=="" (
set NSE=+noise Random
) else (
set NSE=+noise %fnNOISE_TYPE%
)

if "%fnGRAY%"=="1" (
set "sBASE_COL=gray^^^(50%%^^^)"
set sONE_CHAN=-channel R -separate +channel
) else (
set "sBASE_COL=rgb^^^(50%%,50%%,50%%^^^)"
set sONE_CHAN=
)

if "%fnAUTO_LEVEL_EACH%"=="1" (
set sAUTO_EACH=-auto-level
) else if "%fnAUTO_LEVEL_EACH%"=="2" (
set sAUTO_EACH=-channel RGB -auto-level +channel
) else (
set sAUTO_EACH=
)

if "%fnAUTO_LEVEL_END%"=="" set fnAUTO_LEVEL_END=1

if "%fnAUTO_LEVEL_END%"=="1" (
set sAUTO_END=-auto-level
) else if "%fnAUTO_LEVEL_END%"=="2" (
set sAUTO_END=-channel RGB -auto-level +channel
) else (
set sAUTO_END=
)

if "%fnAUTO_GAM_END%"=="" set fnAUTO_GAM_END=1

if "%fnAUTO_GAM_END%"=="1" (
set sAUTO_END=%sAUTO_END% -auto-gamma
)

if "%fnABS_NSE%"=="1" (
set sABS=-solarize 50%%%% -negate
) else (
set sABS=
)

if "%fnA_FRM_NUM%"=="" (
set RD_PYR=
set WR_PYR=
if "%fnA_COLCYCMETH%"=="" set fnA_COLCYCMETH=null
call %PICTBAT%colCyclStr !fnA_COLCYCMETH! 0
) else (
if "%fnA_COLCYCMETH%"=="" set fnA_COLCYCMETH=sinarc2o
if "%fnA_FRM_NUM%"=="0" (
set RD_PYR=
set WR_PYR=1
set ZM_F=1
set DX_F=0
set DY_F=0
rem  ) else (
rem Wrong place! fnInit is run _before_ we know an actual frame number.
rem But it may be called with fnA_FRM_NUM=0, to initialise for an animation.
rem    set RD_PYR=1
rem    set WR_PYR=
rem
rem    call xyCoord %fnWW% %fnHH% %fnA_DX% %fnA_DY%
rem
rem    for /F "usebackq" %%L in (`%IM%identify ^
rem      %PREC% ^
rem      -format "ZM_F=%%[fx:pow(%fnA_ZM%,%fnA_FRM_NUM%)]\nDX_F=%%[fx:!xycX!*%fnA_FRM_NUM%]\nDY_F=%%[fx:!xycY!*%fnA_FRM_NUM%]" ^
rem      xc:`) do set %%L
rem
rem    if "%fnTXT_OUT%"=="1" echo fnA_FRM_NUM=%fnA_FRM_NUM% ZM_F=!ZM_F! DX_F=!DX_F! DY_F=!DY_F!
)
)

if not "%fnRD_PYR%"=="" set RD_NG=%fnRD_PYR%
if not "%fnWR_PYR%"=="" set WR_PYR=%fnWR_PYR%

if not "%fnTXT_OUT%"=="" set pyTXT_OUT=%fnTXT_OUT%

rem No endlocal```

### fnOne.bat

```rem Makes one fractal noise image.
rem Assumes fnInit.bat has been called.

@rem %1 output file. [fn.png]

@rem No compulsory parameters

@setlocal enabledelayedexpansion

@call echoOffSave

rem call %PICTBAT%setInOut %1 fno

set TMP_BAT=%fnPREFIX%fn.bat
set TMP_SCR=%fnPREFIX%fn.scr
del %TMP_SCR% 2>nul

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

set PREC=-precision 15

( echo %fnIM%convert
rem echo -compose Mathematics -define compose:args=0,1,1,-0.5
REM echo -compose Mathematics -define compose:args=0,0.5,0.5,0
if not "%fnFILTER%"=="" echo -filter %fnFILTER%
if not "%fnSEED%"=="" echo -seed %fnSEED%
echo -compose Plus -virtual-pixel Tile %PREC%
) >%TMP_BAT%

if "%NUM_OCTAVES%"=="" (
echo %0: NUM_OCTAVES has no value
exit /B 1
)

:: ------------------ Loop through octaves------------------
set sWR_PYR=
set sRD_PYR=
set sTILE=

set PYR_FILE=%fnPREFIX%fnpyr%fnNG_EXT%
rem echo PYR_FILE=!PYR_FILE! RD_PYR=%RD_PYR% WR_PYR=%WR_PYR%

set sDBG_GRD=
set sDBG_FULL=

set BLK_SZ=%BLK_MIN%

set /A Nm1=%NUM_OCTAVES%-1

rem FIXME?: we auto-each both in sRD_PYR and after PROC_EACH.

for /L %%N in (0,1,%Nm1%) do (

if "%RD_PYR%"=="1" (
set sRD_PYR=%PYR_FILE%[%%N]
) else (
set sRD_PYR=-size !N_BLK_W.%%N!x!N_BLK_H.%%N! xc:%sBASE_COL% %NSE% %sLIN% %sAUTO_EACH%
if "%WR_PYR%"=="1" set sWR_PYR=+write mpr:GRD%%N
)

rem echo sRD_PYR=!sRD_PYR!  sWR_PYR=!sWR_PYR!

if "%fnTILE%"=="1" (

for /F "usebackq" %%L in (`%IM%identify ^
%PREC% ^
-format "FWi=%%[fx:%fnWW%/!N_BLK_W.%%N!]\nFHt=%%[fx:%fnHH%/!N_BLK_H.%%N!]" ^
xc:`) do set %%L

set sTILE=-virtual-pixel Tile +distort SRT "0,0,!FWi!,!FHt!,0,0,0" +repage

rem FIXME: when we get two distort SRTs, we should merge.

rem echo sTILE=!sTILE!
)

set sANIM=!ccsImStr! -virtual-pixel tile -distort SRT 0,0,1,0,0,0

if not "%fnA_FRM_NUM%"=="" (
call xyCoord %fnWW% %fnHH% %fnA_DX% %fnA_DY%

for /F "usebackq" %%L in (`%IM%identify ^
%PREC% ^
-format "ZM_F=%%[fx:pow^(%fnA_ZM%,%fnA_FRM_NUM%^)]\nDX_F=%%[fx:!xycX!*%fnA_FRM_NUM%]\nDY_F=%%[fx:!xycY!*%fnA_FRM_NUM%]" ^
xc:`) do set %%L

if "%fnTXT_OUT%"=="1" echo fnA_FRM_NUM=%fnA_FRM_NUM% ZM_F=!ZM_F! DX_F=!DX_F! DY_F=!DY_F!

for /F "usebackq" %%L in (`%IM%identify ^
%PREC% ^
-format "COL=%%[fx:%fnA_COL%*%fnA_FRM_NUM%*!AN_AMP.%%N!]\nDX=%%[fx:!DX_F!*!AN_AMP.%%N!*!N_BLK_W.%%N!/%fnWW%]\nDY=%%[fx:!DY_F!*!AN_AMP.%%N!*!N_BLK_H.%%N!/%fnHH%]" ^
xc:`) do set %%L

rem echo %0: N=%%N AN_AMP.%%N=!AN_AMP.%%N!  COL=!COL! DX=!DX! DY=!DY!

call %PICTBAT%colCyclStr %fnA_COLCYCMETH% !COL!
if ERRORLEVEL 1 echo %0: Bad colCyclStr && exit /B 1

set sANIM=!ccsImStr! -virtual-pixel tile -distort SRT !DX!,!DY!,1,0,0,0

if "%fnTXT_OUT%"=="1" echo .   AN_AMP.%%N=!AN_AMP.%%N! sANIM=!sANIM!
)

if "%fnDEBUG%"=="1" (
set sDBG_GRD=+write %fnPREFIX%grd_%%N.png
set sDBG_FULL=+write %fnPREFIX%full_%%N.png
)

echo ^( !sRD_PYR! !sWR_PYR! !sDBG_GRD! !sANIM! !sTILE! -resize "%fnWW%x%fnHH%^^^!" !sDBG_FULL! %fnPROC_EACH% %sAUTO_EACH% %sABS% !sLEVEL.%%N! ^) >>%TMP_SCR%

if "%fnTXT_OUT%"=="1" echo -format "levels %%N: %%[fx:minima] %%[fx:maxima]\n" +write info: >>%TMP_SCR%

if %%N GTR 0 echo -compose Mathematics -define compose:args=0,1,1,-0.5 -composite >>%TMP_SCR%

for /F "usebackq" %%L in (`%IM%identify ^
%PREC% ^
-format "BLK_SZ=%%[fx:!BLK_SZ!*%BLK_FAC%]" ^
xc:`) do set %%L
)

( echo @%TMP_SCR%
if "%fnTXT_OUT%"=="1" echo -format "levels before AL: %%%%[fx:minima] %%%%[fx:maxima]\n" +write info:
if not "%sONE_CHAN%"=="" echo %sONE_CHAN%
if not "%sDELIN%"=="" echo %sDELIN%
if not "%sAUTO_END%"=="" echo %sAUTO_END%
echo +depth
echo +write %OUTFILE%

if "%WR_PYR%"=="1" (
echo +delete
for /L %%N in (0,1,%Nm1%) do @(
@echo mpr:GRD%%N
)
echo +write %PYR_FILE%
)
) >>%TMP_BAT%

cPrefix /p0 /i%TMP_BAT% /r" ^"
echo NULL: >>%TMP_BAT%
echo if ERRORLEVEL 1 exit /B 1 >>%TMP_BAT%

rem type %TMP_BAT%
rem type %TMP_SCR%

call %TMP_BAT%
if ERRORLEVEL 1 exit /B 1 >>%TMP_BAT%

if "%fnTXT_OUT%"=="1" (
echo Created %OUTFILE%
if "%WR_PYR%"=="1" echo ... and %PYR_FILE%
)

call echoRestore

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

### xyCoord.bat

```@rem %1 is image width
@rem %2 is image height
@rem %3 is an x-coord,
@rem    possibly fractional, possibly negative, possibly suffixed with c or % or p
@rem %4 is an y-coord, likewise
@rem Returns actual pixel coords, possibly fractional, as xycX and xycY
@rem and as rounded integers in xycXi and xycYi.

@setlocal enabledelayedexpansion

@call echoOffSave

set WW=%1
set HH=%2

set X=%3
set chX=%X:~-1%
set vX=%X:~0,-1%

set Y=%4
set chY=%Y:~-1%
set vY=%Y:~0,-1%

if "%chX%"=="%%" set chX=c
if "%chY%"=="%%" set chY=c

if "%chX%"=="c" (
set vX=%vX%*%1/100
) else if "%chX%"=="p" (
set vX=%vX%*%1
) else (
set vX=%X%
)

if "%chY%"=="c" (
set vY=%vY%*%2/100
) else if "%chY%"=="p" (
set vY=%vY%*%2
) else (
set vY=%Y%
)

for /F "usebackq" %%L in (`%IM%identify ^
-precision 16 ^
-format "vX=%%[fx:%vX%]\nvY=%%[fx:%vY%]\nvXi=%%[fx:int(%vX%+0.5)]\nvYi=%%[fx:int(%vY%+0.5)]" ^
xc:`) do set %%L

if ERRORLEVEL 1 (
echo %0: vX=[%vX%] vY=[%vY%]
exit /B 1
)

call echoRestore

@endlocal & set xycX=%vX%& set xycY=%vY%& set xycXi=%vXi%& set xycYi=%vYi%```

### exHvyBlr.bat

```rem Extrapolate away from heavy blur. Increases local contrast.
rem A bit like tone-mapping, HDR, High Dynamic Range.
@rem
@rem %2 is resize amount (inverse of blur radius) as percentage of image size.
@rem   Small amounts (0.1-5) spread impact over large area. [0.5]
@rem   Large amounts (25-100) give unsharp mask.
@rem %3 is extrapolation as percentage.
@rem   0 is blur; 100 is no effect (unchanged image), 150 is subtle, 15000 is massive.
@rem %4, if given, is output filename.
@rem

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

@setlocal

@call echoOffSave

call %PICTBAT%setInOut %1 ehb

set BLUR_PC=%2
if "%BLUR_PC%"=="." set BLUR_PC=
if "%BLUR_PC%"=="" set BLUR_PC=0.5

set BLEND_PC=%3
if "%BLEND_PC%"=="." set BLEND_PC=
if "%BLEND_PC%"=="" set BLEND_PC=200

if not "%4"=="" set OUTFILE=%4

FOR /F "usebackq" %%L IN (`%IM%identify -format "WW=%%w\nHH=%%h" %INFILE%`) DO set %%L

%IM%convert ^
%INFILE% ^
^( +clone ^
-resize %BLUR_PC%%% -resize "%WW%x%HH%^!" ^
^) ^
+swap ^
-compose Blend -define compose:args=%BLEND_PC% -composite ^
%OUTFILE%

call echoRestore

@endlocal & set ehbOUTFILE=%OUTFILE%```

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

`%IM%identify -version`
```Version: ImageMagick 6.9.9-50 Q16 x64 2018-06-02 http://www.imagemagick.org
Copyright: Copyright (C) 1999-2015 ImageMagick Studio LLC
Visual C++: 180040629
Features: Cipher DPC Modules OpenMP
Delegates (built-in): bzlib cairo flif freetype gslib heic jng jp2 jpeg lcms lqr lzma openexr pangocairo png ps raw rsvg tiff webp xml zlib```

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

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

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 28-August-2015.

Page created 24-Aug-2018 07:56:57.