snibgo's ImageMagick pages

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.

References and Further Reading

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
fn_m1.pngjpg fn_m2.pngjpg

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
fn_m3.pngjpg

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 ^
  ( +clone ) -append ^
  fn_m4.png
fn_m4.pngjpg

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
fn_m5.pngjpg

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.390646219577325 0.606347753109026
levels 2: 0.444449530785077 0.555550469214923
levels 3: 0.354680704966812 0.643945983062486
levels 3: 0.444449530785077 0.555550469214923
levels 4: 0.318989852750439 0.683268482490272
levels 4: 0.44596017395285 0.555550469214923
levels 5: 0.29408712901503 0.703761348897536
levels 5: 0.448630502784772 0.554055085069047
levels 6: 0.262806134126802 0.729823758297093
levels 6: 0.453360799572747 0.551781490806439
levels 7: 0.250019073777371 0.740627145799954
levels 7: 0.448905165178912 0.5505760280766
levels 8: 0.240588998245212 0.772686350804913
levels 8: 0.449286640726329 0.54013885709926
levels before AL: 0.246814679179065 0.773922331578546
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.2-5-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 fn_to_grd_8.miffjpg fn_to_full_8.miffjpg
7 fn_to_grd_7.miffjpg fn_to_full_7.miffjpg
6 fn_to_grd_6.miffjpg fn_to_full_6.miffjpg
5 fn_to_grd_5.miffjpg fn_to_full_5.miffjpg
4 fn_to_grd_4.miffjpg fn_to_full_4.miffjpg
3 fn_to_grd_3.miffjpg fn_to_full_3.miffjpg
2 fn_to_grd_2.miffjpg fn_to_full_2.miffjpg
1 fn_to_grd_1.miffjpg fn_to_full_1.miffjpg
0 fn_to_grd_0.miffjpg fn_to_full_0.miffjpg

The image that was created from these noise grids is:

fn_examp.png fn_examp.pngjpg

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
fn_1.pngjpg
call %PICTBAT%fractNoise ^
  . . . . . . fn_2.png
fn_2.pngjpg
call %PICTBAT%fractNoise ^
  . . . . . . fn_3.png
fn_3.pngjpg

All following examples use the same seed.

set fnSEED=1234

Vary image size

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

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
fn_s1.pngjpg

Omit highest frequencies.

call %PICTBAT%fractNoise ^
  . . 10 . . . fn_s2.png
fn_s2.pngjpg

Omit lowest frequencies.

call %PICTBAT%fractNoise ^
  . . . 64 . . fn_s3.png
fn_s3.pngjpg

Blocks at only one size.

call %PICTBAT%fractNoise ^
  . . 64 64 . . fn_s4.png
fn_s4.pngjpg

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.

fn_bf1.pngjpg

As previous, with auto-levelling.

set fnAUTO_LEVEL_EACH=2

call %PICTBAT%fractNoise ^
  . . . . 1.1 . fn_bf1a.png

set fnAUTO_LEVEL_EACH=
fn_bf1a.pngjpg

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=
fn_bf1b.pngjpg

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.

fn_bf2.pngjpg

Vary power factor

Positive power factors emphasise low-frequency noise.

call %PICTBAT%fractNoise ^
  . . . . . 2 fn_pf1.png
fn_pf1.pngjpg

"1.5".

call %PICTBAT%fractNoise ^
  . . . . . 1.5 fn_pf2.png
fn_pf2.pngjpg

"1" gives the classic halving of amplitude per doubling of frequency.

call %PICTBAT%fractNoise ^
  . . . . . 1 fn_pf3.png
fn_pf3.pngjpg

"0.5".

call %PICTBAT%fractNoise ^
  . . . . . 0.5 fn_pf4.png
fn_pf4.pngjpg

"0" gives equal weight to all frequencies.

call %PICTBAT%fractNoise ^
  . . . . . 0 fn_pf5.png
fn_pf5.pngjpg

"-0.5". Negative factors emphasise high-frequency noise.

call %PICTBAT%fractNoise ^
  . . . . . -0.5 fn_pf6.png
fn_pf6.pngjpg

"-1" doubles the amplitude with each doubling of frequency.

call %PICTBAT%fractNoise ^
  . . . . . -1 fn_pf7.png

Low-frequency noise is almost swamped.

fn_pf7.pngjpg

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
fn_al1.pngjpg
set fnAUTO_LEVEL_EACH=1

call %PICTBAT%fractNoise ^
  . . . . . . fn_al2.png
fn_al2.pngjpg
set fnAUTO_LEVEL_EACH=2

call %PICTBAT%fractNoise ^
  . . . . . . fn_al3.png

set fnAUTO_LEVEL_EACH=
fn_al3.pngjpg

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

See also the Noise page.

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
fn_nt1.pngjpg
set fnNOISE_TYPE=Gaussian
set fnABS_NSE=1

call %PICTBAT%fractNoise ^
  . . . . . . fn_nt1a.png

set fnABS_NSE=
fn_nt1a.pngjpg
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?

fn_nt1b.pngjpg
set fnNOISE_TYPE=Poisson

call %PICTBAT%fractNoise ^
  . . . . . . fn_nt2.png
fn_nt2.pngjpg
set fnNOISE_TYPE=Uniform

call %PICTBAT%fractNoise ^
  . . . . . . fn_nt3.png

set fnNOISE_TYPE=
fn_nt3.pngjpg
set fnNOISE_TYPE=Impulse

call %PICTBAT%fractNoise ^
  . . . . . . fn_nt4.png

set fnNOISE_TYPE=
fn_nt4.pngjpg

"+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
fn_f1.pngjpg
set fnFILTER=Box

call %PICTBAT%fractNoise ^
  . . . . . 1 fn_f2.png
fn_f2.pngjpg
set fnFILTER=Point

call %PICTBAT%fractNoise ^
  . . . . . . fn_f3.png

Point is identical to Box.

fn_f3.pngjpg
set fnFILTER=Cubic

call %PICTBAT%fractNoise ^
  . . . . . . fn_f4.png
fn_f4.pngjpg
set fnFILTER=Triangle

call %PICTBAT%fractNoise ^
  . . . . . . fn_f5.png

set fnFILTER=
fn_f5.pngjpg

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
fn_g1.pngjpg
call %PICTBAT%fractNoise ^
  . . 10 40 1.1 . fn_g2.png

set fnGRAY=
fn_g2.pngjpg

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:

set fnA_COLCYCMETH=null

call %PICTBAT%fractNoise ^
  . . . . . . fn_cm1.png
fn_cm1.pngjpg
set fnA_COLCYCMETH=sinarc2o

call %PICTBAT%fractNoise ^
  . . . . . . fn_cm2.png

%IM%compare -metric RMSE ^
  fn_cm1.png fn_cm2.png NULL: 
1.41681 (2.16192e-005)
fn_cm2.pngjpg
set fnA_COLCYCMETH=amsol

call %PICTBAT%fractNoise ^
  . . . . . . fn_cm3.png

%IM%compare -metric RMSE ^
  fn_cm1.png fn_cm3.png NULL: 


set fnA_COLCYCMETH=
9137.04 (0.139422)
fn_cm3.pngjpg

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.497801
fn_lin1.pngjpg
%IM%convert ^
  fn_lin1.png ^
  -format "mean=%%[fx:mean]" ^
  -set colorspace RGB ^
  -colorspace sRGB ^
  +write info: ^
  fn_lin2.png 
mean=0.73078
fn_lin2.pngjpg
set fnLINEAR=1

call %PICTBAT%fractNoise ^
  . . . . . . fn_lin3.png

%IM%convert ^
  fn_lin3.png ^
  -format "mean=%%[fx:mean]" ^
  info: 

set fnLINEAR=
mean=0.499506
fn_lin3.pngjpg

How much difference does fnLINEAR make?

%IM%compare -metric RMSE ^
  fn_lin1.png fn_lin3.png NULL: 
1894.84 (0.0289134)

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
fn_lb1.pngjpg

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.

fn_lb2.pngjpg
call %PICTBAT%fractNoise ^
  . . 250 1000 . . fn_lb3.png
fn_lb3.pngjpg
set fnGRAY=1

call %PICTBAT%fractNoise ^
  . . 1 10 . . fn_lb4.png

set fnGRAY=

Resembles magnified silver-halide film grain
at a constant middle exposure.

fn_lb4.pngjpg

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=
fn_c1_glc.png
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.

fn_c2_glc.png

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
fn_c2a_glc.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.

fn_c2b_glc.png
call %PICTBAT%fractNoise ^
  600 1 4 300 . . fn_c3.png

call %PICTBAT%graphLineCol ^
  fn_c3.png
fn_c3_glc.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
fn_c4_glc.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
fn_c5_glc.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
fn_c6_glc.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=
fn_c7_glc.png

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
fn_po1.pngjpg

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.

fn_po2.pngjpg

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=
fn_po3.pngjpg
set fnPROC_EACH=-evaluate Pow 2

call %PICTBAT%fractNoise ^
  . . . . . . fn_po4.png
fn_po4.pngjpg
set fnPROC_EACH=-threshold 50%%%%

call %PICTBAT%fractNoise ^
  . . . . . . fn_po5.png
fn_po5.pngjpg
set fnPROC_EACH=-channel RGB -threshold 50%%%% +channel

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

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=
fn_po7.pngjpg

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

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

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
fn_v1.pngjpg
call %PICTBAT%fractNoise ^
  1800 400 . . . . fn_v1a.png

%IM%convert ^
  fn_v1a.png ^
  -resize "600x400^!" ^
  fn_v1a.png
fn_v1a.pngjpg
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
fn_v1b.pngjpg

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
fn_v1ca.pngjpg

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.

fn_v1cb.pngjpg
set fnGRAY=1

call %PICTBAT%fractNoise ^
  . . 10 . . . fn_v2.png
fn_v2.pngjpg
set fnGRAY=1
set fnNOISE_TYPE=Impulse
set fnTILE=1

call %PICTBAT%fractNoise ^
  . . . . . . fn_vpl2.png

set fnTILE=
set fnNOISE_TYPE=
fn_vpl2.pngjpg

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
fn_vpl2b.pngjpg
%IM%convert ^
  fn_v2.png ^
  ( -size 1x500 gradient: -rotate 90 ^
    -duplicate 19 +append ) ^
  -clut ^
  -morphology edgein diamond:1 ^
  -threshold 40%% ^
  fn_v2a.png

set fnGRAY=
fn_v2a.png
set fnGRAY=1

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

Increase local contrast

call %PICTBAT%exHvyBlr ^
  fn_vg1.png . . fn_vg1b.png 
fn_vg1b.pngjpg

Apply a simple clut

%IM%convert ^
  fn_vg1b.png ^
  ( xc:#008 xc:#0f0 +append ) ^
  -clut ^
  fn_vg1c.png
fn_vg1c.pngjpg

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 ) ^
  -clut ^
  fn_w1a.png

set fnGRAY=
fn_w1a.pngjpg

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 ) ^
  -clut ^
  fn_w1b.png
fn_w1b.pngjpg

Wood = Colouring * Grain

%IM%convert ^
  fn_w1a.png ^
  fn_w1b.png ^
  -compose Multiply -composite ^
  fn_w1c.png
fn_w1c.pngjpg

Camouflage pattern. Integer interpolation.

%IM%convert ^
  fn_g2.png ^
  -auto-level -auto-gamma ^
  ( xc:#131 xc:#882 xc:#481 +append ) ^
  -interpolate Integer ^
  -clut ^
  fn_cam1.png
fn_cam1.pngjpg

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 ) ^
  -interpolate Integer ^
  -clut ^
  fn_cam2.png
fn_cam2.pngjpg

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 ) ^
  -clut ^
  fn_cam3.png
fn_cam3.pngjpg

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
fn_grn1.pngjpg

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
fn_grn2.pngjpg

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 ^
  ( +clone ) -append ^
  fn_t1t.png
fn_t1.pngjpg fn_t1t.pngjpg
call %PICTBAT%fractNoise ^
  300 200 . 300 . 1 fn_t2.png

%IM%convert ^
  fn_t2.png ^
  ( +clone ) +append ^
  ( +clone ) -append ^
  fn_t2t.png
fn_t2.pngjpg fn_t2t.pngjpg
set fnGRAY=1

call %PICTBAT%fractNoise ^
  300 200 . . . . fn_t3.png

%IM%convert ^
  fn_t3.png ^
  ( +clone ) +append ^
  ( +clone ) -append ^
  fn_t3t.png

set fnGRAY=
set fnTILE=
fn_t3.pngjpg fn_t3t.pngjpg

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
fn_t1tp1.pngjpg
%IM%convert ^
  fn_t1t.png ^
  -distort polar 0,0 ^
  fn_t1tp2.png
fn_t1tp2.pngjpg
%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
fn_t1tp3.pngjpg
%IM%convert ^
  fn_t3t.png ^
  -distort polar -1,0 ^
  fn_t3tp1.png
fn_t3tp1.pngjpg
%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
fn_t3tp2.pngjpg
%IM%convert ^
  fn_t3tp2.png ^
  -posterize 4 ^
  fn_t3tp3.png
fn_t3tp3.pngjpg
call %PICTBAT%lgstConnComp ^
  fn_t3tp3.png ^
  fn_t3tp3c.png
fn_t3tp3c.pngjpg

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
fn_t3tp2b.pngjpg

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 ^
  -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
fn_t3tp2c.pngjpg
%IM%convert ^
  fn_t3tp2c.png ^
  -shade 135x30 ^
  fn_t3tp2d.png
fn_t3tp2d.pngjpg

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
fn_to1.pngjpg

With tiling.

set fnTILE=1

call %PICTBAT%fractNoise ^
    . . . . . 1 fn_to2.png

set fnTILE=
fn_to2.pngjpg

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.

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 ^
  -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=
fn_cld_a.pngjpg fn_cld_b.pngjpg fn_cld_c.pngjpg fn_cld_d.pngjpg

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
fn_disp1.pngjpg
%IM%convert ^
  %SRC2% ^
  %SRC1% ^
  -compose Displace ^
    -set option:compose:args 3%%x3%% ^
    -composite ^
  fn_disp2.png
fn_disp2.pngjpg
%IM%convert ^
  %SRC2% ^
  fn_disp1.png ^
  -compose Displace ^
    -set option:compose:args 3%%x3%% ^
    -composite ^
  fn_disp3.png
fn_disp3.pngjpg
%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.

fn_disp4.pngjpg

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
fn_disp5.pngjpg

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
fn_disp6.pngjpg

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
fn_disp7.pngjpg

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.

fn_disp8.pngjpg

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
fn_disp9.pngjpg

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 ^
  -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
fn_fire_base.pngjpg fn_fire.pngjpg

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.311MB 0.000u 0:00.000
fn_rfn.tiff[1] TIFF 300x200 300x200+0+0 32-bit sRGB 3.311MB 0.016u 0:00.014
fn_rfn.tiff[2] TIFF 150x100 150x100+0+0 32-bit sRGB 3.311MB 0.016u 0:00.014
fn_rfn.tiff[3] TIFF 75x50 75x50+0+0 32-bit sRGB 3.311MB 0.016u 0:00.014
fn_rfn.tiff[4] TIFF 38x25 38x25+0+0 32-bit sRGB 3.311MB 0.016u 0:00.014
fn_rfn.tiff[5] TIFF 19x13 19x13+0+0 32-bit sRGB 3.311MB 0.016u 0:00.014
fn_rfn.tiff[6] TIFF 9x6 9x6+0+0 32-bit sRGB 3.311MB 0.016u 0:00.014
fn_rfn.tiff[7] TIFF 5x3 5x3+0+0 32-bit sRGB 3.311MB 0.016u 0:00.014
fn_rfn.tiff[8] TIFF 600x400 600x400+0+0 32-bit Grayscale Gray 3.311MB 0.016u 0:00.014
Octave Grid Grid, resized up Histogram of resized grid Difference
7 fn_rfn_grd_7.miffjpg fn_rfn_full_7.miffjpg fn_rfn_full_7_h_glc.png fn_rfn_diff_7.miffjpg
6 fn_rfn_grd_6.miffjpg fn_rfn_full_6.miffjpg fn_rfn_full_6_h_glc.png fn_rfn_diff_6.miffjpg
5 fn_rfn_grd_5.miffjpg fn_rfn_full_5.miffjpg fn_rfn_full_5_h_glc.png fn_rfn_diff_5.miffjpg
4 fn_rfn_grd_4.miffjpg fn_rfn_full_4.miffjpg fn_rfn_full_4_h_glc.png fn_rfn_diff_4.miffjpg
3 fn_rfn_grd_3.miffjpg fn_rfn_full_3.miffjpg fn_rfn_full_3_h_glc.png fn_rfn_diff_3.miffjpg
2 fn_rfn_grd_2.miffjpg fn_rfn_full_2.miffjpg fn_rfn_full_2_h_glc.png fn_rfn_diff_2.miffjpg
1 fn_rfn_grd_1.miffjpg fn_rfn_full_1.miffjpg fn_rfn_full_1_h_glc.png fn_rfn_diff_1.miffjpg
0 fn_rfn_grd_0.miffjpg fn_rfn_full_0.miffjpg fn_rfn_full_0_h_glc.png fn_rfn_diff_0.miffjpg

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)
fn_rfn_recon.pngjpg

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 fn_rfn_sum_grd_7.miffjpg fn_rfn_sum_full_7.miffjpg fn_rfn_sum_full_7_h_glc.png fn_rfn_sum_diff_7.miffjpg
6 fn_rfn_sum_grd_6.miffjpg fn_rfn_sum_full_6.miffjpg fn_rfn_sum_full_6_h_glc.png fn_rfn_sum_diff_6.miffjpg
5 fn_rfn_sum_grd_5.miffjpg fn_rfn_sum_full_5.miffjpg fn_rfn_sum_full_5_h_glc.png fn_rfn_sum_diff_5.miffjpg
4 fn_rfn_sum_grd_4.miffjpg fn_rfn_sum_full_4.miffjpg fn_rfn_sum_full_4_h_glc.png fn_rfn_sum_diff_4.miffjpg
3 fn_rfn_sum_grd_3.miffjpg fn_rfn_sum_full_3.miffjpg fn_rfn_sum_full_3_h_glc.png fn_rfn_sum_diff_3.miffjpg
2 fn_rfn_sum_grd_2.miffjpg fn_rfn_sum_full_2.miffjpg fn_rfn_sum_full_2_h_glc.png fn_rfn_sum_diff_2.miffjpg
1 fn_rfn_sum_grd_1.miffjpg fn_rfn_sum_full_1.miffjpg fn_rfn_sum_full_1_h_glc.png fn_rfn_sum_diff_1.miffjpg
0 fn_rfn_sum_grd_0.miffjpg fn_rfn_sum_full_0.miffjpg fn_rfn_sum_full_0_h_glc.png fn_rfn_sum_diff_0.miffjpg

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.197284 max=0.801129

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)
fn_rfn_sum_recon.pngjpg

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
@rem See also hvyBlrDiff.bat

@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.2-5 Q16 x64 2015-10-31 http://www.imagemagick.org
Copyright: Copyright (C) 1999-2015 ImageMagick Studio LLC
License: http://www.imagemagick.org/script/license.php
Visual C++: 180031101
Features: Cipher DPC Modules OpenMP 
Delegates (built-in): bzlib cairo freetype jng jp2 jpeg lcms lqr openexr pangocairo png ps 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 07-Mar-2016 15:10:54.

Copyright © 2016 Alan Gibson.