snibgo's ImageMagick pages

Gradients Cookbook

This page is mostly about two-dimensional greyscale gradients, ranging from black to white, that have different values for both x and y dimensons. They are useful as masks for processing photographs, for video transitions, and a variety of graphics effects.

Introduction

I use ImageMagick gradients as masks for processing photographs and for video transitions. Gradients are usually monochrome images with smooth transitions from black through gray to white.

For one-dimensional gradients, see Clut cookbook.

The scripts are all Windows "bat" syntax, and readily translatable to other languages.

The environment variable %IM% is set to the directory that contains the IM convert program.

The dimensions of the mask are generally taken from the photograph or video frame:

FOR /F "tokens=1,2" %%i ^
IN ('%IM%identify -ping -format "%%w %%h" photo.jpg') ^
DO (
  set WIDTH=%%i
  set HEIGHT=%%j
)

... or ...

FOR /F "usebackq" %%L IN (`%IM%identify -ping -format "WIDTH=%%w\nHEIGHT=%%h" %SRC%`) DO set %%L

Masks are used to blend still photographs, such as two versions of one image, with different saturations:

%IM%convert ^
  {photo1} ^
  {photo2} ^
  {mask} ^
  -alpha off ^
  -compose ^
  {outphoto}

... or ...

%IM%convert ^
  {photo1} ^
  ( {photo2} {mask} -alpha off -compose Copy-Opacity -composite ) ^
  -compose Over -composite ^
  {outphoto}

Where the mask is black, Copy-Opacity makes the result transparent, so photo1 becomes visible. Where the mask is white the result is opaque, so photo2 is visible.

For videos, using clippings option TransitionMask, we modulate the influence of the mask over time:

%IM%convert ^
  {clip1_frame} ^
  ( {clip2_frame} ^
    ( {mask} -alpha off -level %lev1%x%lev2%%% ) ^
    -compose Copy_Opacity -composite ^
  ) ^
  -compose Over -composite ^
  {outclip_frame}

... where 0 <= lev1 <= lev2 <= 100.

The above command is equivalent to:

%IM%convert ^
  {clip2_frame} ^
  ( {mask} -alpha off -level %lev1%x%lev2%%% ) ^
  -compose Copy_Opacity -composite ^
  {clip1_frame} ^
  -compose Dst_Over -composite ^
  {outclip_frame}

Basics

The basic gradient is white at the top, black at the bottom:

set WIDTH=300
set HEIGHT=200

%IM%convert ^
  -size %WIDTH%x%HEIGHT% ^
  gradient: ^
  gr_baseGrad.png
gr_baseGrad.png

50% gray occurs at the position mid-way between black and white. Similarly for all percentages. So thresholding at x% results in x% of pixels being black, and modulating a video threshold at a constant speed results in a constant speed of transition.

For a sideways transition, we rotate, so we need to invert the initial width and height.

%IM%convert ^
  -size %HEIGHT%x%WIDTH% ^
  gradient: ^
  -rotate 90 ^
  gr_baseGradS.png
gr_baseGradS.png

This is used for a basic left-to-right transition:

%IM%convert ^
  -size %WIDTH%x%HEIGHT% ^
  xc:Red ^
  xc:Green ^
  ( gr_baseGradS.png -level 20%%x30%% ) ^
  -composite ^
  gr_baseEx.png
gr_baseEx.png

A radial-gradient transitions from the centre outwards.

%IM%convert ^
  -size %WIDTH%x%HEIGHT% ^
  radial-gradient: ^
  gr_rad.png
gr_rad.png

Note that "radial-gradient:" applies only to max(width,height) from the image centre. To fade all the way to the corners, start with a square image that is as wide as the diagonal we want. The contours of the gradient are circular.

FOR /F %%i ^
IN ('%IM%identify ^
  -ping ^
  -format "%%[fx:int(sqrt(%WIDTH%*%WIDTH%+%HEIGHT%*%HEIGHT%)+0.5)]" ^
  xc:') ^
DO set DIM=%%i

%IM%convert ^
  -size %DIM%x%DIM% ^
  radial-gradient: ^
  -gravity Center -crop %WIDTH%x%HEIGHT%+0+0 +repage ^
  gr_rad2.png
gr_rad2.png

A related gradient has equal values at the centre of each edge, so the contours are elliptical.

FOR /F %%i ^
IN ('%IM%identify ^
  -ping ^
  -format "%%[fx:max(%WIDTH%,%HEIGHT%)]" ^
  xc:') ^
DO set MAX=%%i

FOR /F %%i ^
IN ('%IM%identify ^
  -ping ^
  -format "%%[fx:int(%MAX%*sqrt(2)+0.5)]" ^
  xc:') ^
DO set DIM=%%i

%IM%convert ^
  -size %DIM%x%DIM% ^
  radial-gradient: ^
  -gravity Center -crop %MAX%x%MAX%+0+0 +repage ^
  -resize "%WIDTH%x%HEIGHT%^!" ^
  gr_rad3.png
gr_rad3.png

We can create a radial gradient of diameter 100, on a larger image, centering the gradient at (230,130). The parameter to "-extent" is WxH-X-Y where (X,Y) is the required coordinate of the top-left of the image so far. So X = 230-100/2 = 180 and Y = 130-100/2 = 80.

%IM%convert ^
  -size 100x100 ^
  radial-gradient: ^
  -background #000 ^
  -extent %WIDTH%x%HEIGHT%-180-80 ^
  gr_rad4.png
gr_rad4.png

We can readily chop and rearrange masks:

%IM%convert ^
  gr_baseGrad.png ^
  -crop 2x1@ ^
  ( -clone 0 -negate ) ^
  -delete 0 ^
  +append +repage ^
  gr_baseGrad2.png
gr_baseGrad2.png
%IM%convert ^
  gr_baseGradS.png ^
  -crop 1x2@ ^
  ( -clone 0 -negate ) ^
  -delete 0 ^
  -append +repage ^
  gr_baseGradS2.png
gr_baseGradS2.png
%IM%convert ^
  gr_baseGradS2.png ^
  ( +clone -negate )^
  +append +repage ^
  -resize "%WIDTH%x%HEIGHT%^!" ^
  gr_baseGradS4.png
gr_baseGradS4.png
set NUM=3
set ANG=90

%IM%convert ^
  -size %WIDTH%x%HEIGHT% ^
  gradient: ^
  -function Sinusoid %NUM%,%ANG%,0.5,0.5 ^
  gr_sinus.png
gr_sinus.png
set NUM=3
set ANG=90

%IM%convert ^
  -size %WIDTH%x%HEIGHT% ^
  xc: ^
  -sparse-color barycentric "0,0 gray80 %%[fx:w-1],%%[fx:h-1] gray20" ^
  -function Sinusoid %NUM%,%ANG%,0.5,0.5 ^
  -function Sinusoid %NUM%,%ANG%,0.5,0.5 ^
  gr_sinus2.png
gr_sinus2.png
%IM%convert ^
  gr_baseGradS.png ^
  ( +clone -negate ) ^
  +append ^
  -resize "%WIDTH%x%HEIGHT%^!" ^
  -depth 16 ^
  gr_barnDoor.png
gr_barnDoor.png
%IM%convert ^
  gr_sinus.png ^
  ( gr_barnDoor.png -level 20%%,40%% ) ^
  -compose CopyOpacity -composite ^
  -write gr_sb.png ^
  -background Gray -alpha Remove ^
  gr_sinusBd.png

This might be useful for a shimmy effect, where pixels are displaced to left or right.

gr_sinusBd.png

We can quantise (this one calls for a sound FX of a cell-door slamming at each transition). This is also useful for checking that a complex gradient is equally spaced, or that it proceeds in a straight line.

%IM%convert ^
  gr_baseGradS2.png ^
  -posterize 10 ^
  gr_baseQ.png
gr_baseQ.png

The patches at each end are smaller because "-posterize" uses gray levels of 0%, 11.1%, 22.2% etc, so levels of 0 to 5.5% go into the first band, where the second band takes levels of 5.5% to 16.6%.

If we want ten levels of the same width from black to white, we can use a trick from the Clut Cookbook:

%IM%convert ^
  gr_baseGradS2.png ^
  ( -size 1x256 gradient: -rotate 90 ^
    -fx "int(u*10)/9" ^
  ) ^
  -interpolate NearestNeighbor ^
  -clut ^
  gr_baseQ2.png
gr_baseQ2.png

The script could be simpler, by applying "-fx" directly to the source file istead of via a clut. But that would be slower: one "-fx" per source pixel insted of one per clut pixel, and a clut is (usually) much smaller than source files.

"-interpolate NearestNeighbor" ensures that every pixel is one of the ten levels, avoiding any interpolation at a boundary. However, boundary interpolation (aka anti-aliasing) may be preferred.

Or, more simply, we can use "-colors". But this doesn't give us ten colours. It merely guarantees there will be ten colours or fewer.

%IM%convert ^
  gr_baseGradS2.png ^
  +dither -colors 10 ^
  gr_baseQ3.png
gr_baseQ3.png

Arcs

We can bend linear gradients into arcs.

FOR /F %%i ^
IN ('%IM%identify -format "%%[fx:max(%WIDTH%,%HEIGHT%)/2]" xc:') ^
DO (
  set RMAX=%%i
)

set /A POL_WI=RMAX*3
set /A POL_HT=RMAX

Note we invert the usual height and width, because we will rotate.

%IM%convert ^
  -size %POL_HT%x%POL_WI% ^
  gradient: ^
  -rotate 90 ^
  -distort Arc ^"180 0 %HEIGHT% 0^" ^
  +repage ^
  gr_arc180Mask.png

%IM%identify gr_arc180Mask.png 
gr_arc180Mask.png PNG 402x202 402x202+0+0 16-bit sRGB 95.7KB 0.016u 0:00.001
gr_arc180Mask.png
%IM%convert ^
  -size %POL_HT%x%POL_WI% ^
  gradient: ^
  -rotate 90 ^
  -distort Arc ^"90 45 %HEIGHT% 0^" ^
  -flip ^
  +repage ^
  gr_arc90Mask.png

%IM%identify gr_arc90Mask.png 
gr_arc90Mask.png PNG 202x202 202x202+0+0 16-bit sRGB 53.6KB 0.016u 0:00.000
gr_arc90Mask.png
%IM%convert ^
  gr_arc90Mask.png ^
  ( +clone -flop ) ^
  +append ^
  gr_arc902Mask.png
gr_arc902Mask.png
%IM%convert ^
  gr_arc90Mask.png ^
  ( +clone -flop ) ^
  +swap ^
  +append ^
  gr_arc902bMask.png
gr_arc902bMask.png

The "rising curtain" effect, gr_arc902Mask.png, will have straight lines that meet in the centre-line at an angle. This can be seen when we posterize it:

%IM%convert ^
  gr_arc902Mask.png ^
  -posterize 10 ^
  gr_arc902MaskQ.png
gr_arc902MaskQ.png

If we prefer a curved line, we can use a modified version of gr_arc90Mask.png, using an absolute displacement map, displacing only horizontally. The map is of a kind of ripple such that f(0)=0, f'(0)=1, f(1)=1 but f'(1)=1. The first quarter of a sin curve is such a function.

FOR /F "tokens=1,2" %%i ^
IN ('%IM%identify -ping -format "%%w %%h" gr_arc90Mask.png') ^
DO (
  set wi=%%i
  set ht=%%j
)

We write and display gr_preSin.png merely for comparison with gr_qtrSin.png.

%IM%convert ^
  -size %wi%x%ht% ^
  gradient: -rotate 90 ^
  -write gr_preSin.png ^
  -function sinusoid 0.25,0,1,0 ^
  gr_qtrSin.png

gr_qtrSin.png is the absolute displacement map

gr_preSin.png
gr_qtrSin.png

Apply the absolute displacement map.

%IM%convert ^
  gr_arc90Mask.png ^
  gr_qtrSin.png ^
  -fx "p{v*w,j}" ^
  gr_arc90qsMask.png
gr_arc90qsMask.png

Demonstrate it:

%IM%convert ^
  gr_arc90qsMask.png ^
  -posterize 10 ^
  gr_arc90qsMaskD.png
gr_arc90qsMaskD.png

gr_qtrSin is used as an absolute displacement mask. IM has no operators to process absolute displacement masks, so we need to use "-fx", which is slow. As we don't do this processing for every frame, speed doesn't matter. If we needed the processing for every frame, we might convert it to a relative displacement mask. See Displacement maps.

With greyRectMask.bat, we can choose the grey levels for the corners:

call %PICTBAT%greyRectMask.bat ^
  %WIDTH% %HEIGHT% 75 90 20 30
greyRectMask.png

A diagonal gradient is simple, eg top-left white to bottom-right black:

%IM%convert ^
  -size %WIDTH%x%HEIGHT% xc:white ^
  -sparse-color barycentric "0,0 white %%[fx:w-1],%%[fx:h-1] Black" ^
  gr_diag.png
gr_diag.png
call %PICTBAT%ellipMaskCornCol.bat ^
  %WIDTH% %HEIGHT% 10 10 10  75 75 75
ellipMaskCornCol.png
call %PICTBAT%ellipMaskCorn.bat ^
  %WIDTH% %HEIGHT% 10 75
ellipMaskCorn.png

We can simulate pond ripples. Note that "radial-gradient:" applies only to max(width,height) from the image centre, so we build a square image that is as wide as the diagonal we want.

We can vary ang from zero to 360 for one cycle, or zero to (360 * %NUM%) = (360*3) = 1080 for three cycles, from centre to corner.

set NUM=3
set ANG=90

FOR /F %%i ^
IN ('%IM%identify ^
  -ping ^
  -format "%%[fx:int(sqrt(%WIDTH%*%WIDTH%+%HEIGHT%*%HEIGHT%)+0.5)]" ^
  xc:') ^
DO set DIM=%%i

%IM%convert ^
  -size %DIM%x%DIM% ^
  radial-gradient: ^
  -function Sinusoid %NUM%,%ANG%,0.5,0.5 ^
  -gravity Center -crop %WIDTH%x%HEIGHT%+0+0 +repage ^
  gr_pond.png
gr_pond.png

The next mask is useful for burning-in (darkening) the bottom edge of a photo, with the effect increasing slightly towards the corners.

call %PICTBAT%baseMask.bat ^
  %WIDTH% %HEIGHT% 25
baseMask.png

A clock

The resulting height and width will be twice the polar argument RMAX, plus one or two so we should crop the results (not shown here).

for /F "usebackq" %%i in (`%IM%identify ^
  -format "%%[fx:max(%WIDTH%,%HEIGHT%)/2]" ^
  xc:`) do set RMAX=%%i

set /A POL_WI=RMAX*3
set /A POL_HT=RMAX

rem Note we invert the usual height and width, because we will rotate.

%IM%convert ^
  -size %POL_HT%x%POL_WI% gradient: ^
  -rotate -90 ^
  +distort Polar ^"%RMAX%^" ^
  +repage ^
  gr_clock.png

%IM%identify -ping -format "%%w %%h" gr_clock.png 
302 302
gr_clock.pngjpg
%IM%convert -size %POL_HT%x%POL_WI% gradient: ^
  -rotate -90 ^
  +distort Polar ^"%RMAX%,0 0.5,0.5^" ^
  +repage ^
  gr_clock2.png

%IM%identify -ping -format "%%w %%h" gr_clock2.png 
301 301
gr_clock2.pngjpg
%IM%convert -size %POL_HT%x%POL_WI% gradient: ^
  -rotate 90 ^
  ( +clone ) ( +clone ) +append +repage ^
  +distort Polar ^"%RMAX%,0 0.5,0.5^" ^
  +repage -flop ^
  gr_clock3.png

%IM%identify -ping -format "%%w %%h" gr_clock3.png 
301 301
gr_clock3.pngjpg
%IM%convert -size %POL_HT%x%POL_WI% gradient: ^
  -function Sinusoid 4,90 ^
  -rotate 90 ^
  +distort Polar ^"%RMAX%,0 0.5,0.5^" ^
  +repage -flop ^
  gr_clock4.png

%IM%identify -ping -format "%%w %%h" gr_clock4.png 
301 301
gr_clock4.pngjpg

Aside: arctan

An alternative method for the basic gradient uses "-fx atan2":

%IM%convert ^
  -size %WIDTH%x%WIDTH% gradient: -rotate -90 ^
  ( +clone -rotate -90 ) ^
  +write gr_2grad.png ^
  -fx "atan2(u-0.5,v-0.5)/2/pi+0.5" ^
  gr_clock2b.png

%IM%convert ^
  -size %WIDTH%x%WIDTH% gradient: -rotate 180 ^
  ( +clone -rotate -90 ) ^
  +write gr_2grad.png ^
  -fx "atan2(u-0.5,v-0.5)/2/pi+0.5" ^
  -evaluate AddModulus 75%% ^
  gr_clock2b.png

%IM%identify -ping -format "%%w %%h" gr_clock2b.png 
300 300
gr_clock2b.pngjpg

However, "-fx" is too slow for large images. An alternative uses -function ArcTan, generalised to find the arctan(x,y) for any pair of input files, is:

rem Note: 1/pi = 0.3183098861837907
rem This needs HDRI.

%IMDEV%convert ^
  gr_2grad-0.png gr_2grad-1.png ^
  -evaluate Subtract 50%% ^
  ( -clone 1 -threshold 0 +write mpr:MASK +delete ) ^
  -define compose:clamp=off ^
  -compose DivideSrc -composite ^
  -function ArcTan 0.3183098861837907,0,0.5,0.5 ^
  -mask mpr:MASK -evaluate AddModulus 50%% +mask ^
  -evaluate AddModulus 75%% ^
  gr_clock2c.png
gr_clock2c.pngjpg

The Dst and Src to the divide range from -50% to +50%. The -function ArcTan expression is:

y = range/pi * atan (slope * pi * (u - center)) + bias
  =   0.5/pi * atan (pi/pi *      (u -    0  )) + 0.5
  = 1/(2*pi) * atan(u) + 0.5

atan(u) returns between -pi/2 and +pi/2 radians (-90 and +90 degrees). We multiply by 1/(2*pi) to get a range -0.25 to +0.25, then add 0.5 to get a range +0.25 to +0.75. Where the Src is negative, the answer so far is wrong by pi radians (180 degrees), and we rectify this by adding 0.5 when Src is less than or equal to 0%.

However, when Src==0, DivideSrc returns merely 100%, so the arctan returns 45°, which is wrong. We could special-case this, but it gets a bit messy. Instead, we write a process module: arctan2.

%IMDEV%convert ^
  gr_2grad-0.png gr_2grad-1.png ^
  -evaluate Subtract 50%% ^
  -process 'arctan2 b 0' ^
  -evaluate AddModulus 25%% ^
  gr_clock2d.png
gr_clock2d.pngjpg

We use this process module in the script slopeXYdirn.bat. See "direction" in the Slopes page.

%IM%convert ^
  gr_2grad-0.png gr_2grad-1.png ^
  gr_2grad_tmp.miff

call %PICTBAT%slopeXYdirn ^
  gr_2grad_tmp.miff ^
  gr_clock2e.png ^
  . SUB 25
gr_clock2e.pngjpg

These can usefully be swirled. The swirl effect doesn't extend beyond the inscribed circle, so it might be wise to create a larger image then crop to within that circle: make the width and height of the square equal to the diagonal of the required image.

%IM%convert ^
  gr_clock2.png ^
  -filter Point -interpolate Integer ^
  -swirl 180 ^
  gr_clock2s.png
gr_clock2s.pngjpg
%IM%convert ^
  gr_clock2.png ^
  -filter Point -interpolate Integer ^
  -swirl 540 ^
  gr_clock2sb.png
gr_clock2sb.pngjpg
%IM%convert ^
  gr_clock3.png ^
  -filter Point -interpolate Integer ^
  -swirl 180 ^
  gr_clock3s.png
gr_clock3s.pngjpg
%IM%convert ^
  gr_clock4.png ^
  -filter Point -interpolate Integer ^
  -swirl 180 ^
  gr_clock4s.png
gr_clock4s.pngjpg

Spirals

A different spiral is available by using morphology distance to create the transition. This is somewhat slow.

We create the polygon carefully so it will tile exactly. We want only two colours, so switching off interpolation (+antialias, -filter Point -interpolate Integer) would be good. But this leads to slight weirdness, so we threshold and level.

set wi=300
set ht=300

set /A wim1=%wi%-1
set /A htm1=%ht%-1

set /A swi=%wi%/2
set /A sht=%ht%/2

set /A swip1=%swi%+1

set /A shtm1=%sht%-1
set /A shtm2=%sht%-2

%IM%convert ^
  -size %wi%x%ht% ^
  xc:Black ^
  -fill White ^
  -draw ^"polygon 0,0 %wim1%,%htm1% %swi%,%htm1% 0,%shtm1% ^
          polygon %swip1%,0 %wim1%,0 %wim1%,%shtm2%^" ^
  gr_poly.png

  rem -fill rgb(50%%,50%%,50%%) ^
gr_poly.png

If we wanted more than a single turn of the spiral, we would append:

%IM%convert ^
  gr_poly.png ^
  ( +clone ) ^
  ( +clone ) ^
  +append +repage ^
  gr_poly3.png
gr_poly3.png

If we just wanted spirals, without the morphology transition, we might want "-virtual-pixel tile" before the distort. But that breaks spiral arms at the edges so morphology can't reach the end segments.

%IM%convert ^
  gr_poly.png ^
  +distort Polar ^"%RMAX%,0 0.5,0.5^" ^
  +repage ^
  gr_spiral.png

rem  -channel RGB -threshold 25%% +level 0,50%% ^
gr_spiral.png
%IM%convert ^
  gr_spiral.png -rotate 90 ^
  gr_spiral.png ^
  -compose Difference -composite ^
  -write gr_spiral2a.png ^
  -channel RGB -threshold 50%% ^
  gr_spiral2.png
gr_spiral2.png
FOR /F "tokens=1,2" %%i ^
IN ('%IM%identify -ping -format "%%w %%h" gr_spiral2.png') ^
DO (
  set wi=%%i
  set ht=%%j
)

set /A swi=%wi%/2
set /A sht=%ht%/2

rem set /A cxplus=%swi%+5
rem set /A cyplus=%sht%+5
rem set /A cymin=%sht%-5

%IM%convert ^
  gr_spiral2.png ^
  -draw "point %swi% %sht%" ^
  gr_spiral2.png

%IM%convert ^
  gr_spiral2.png ^
  -negate ^
  -draw "point %swi% %sht%" ^
  gr_spiral2n.png
gr_spiral2n.png

The centre of each mask, (swi,sht), must be black so the morphology will straddle both arms. This also provides a good starting point for the morphology.

"Euclidean:2,10": radius 2 isn't noticably worse than 4 or 7. Increment 10 allows for a spiral length of 6553 pixels.

For 2 Mpix (video) images, each morphology takes about 12 minutes. If the spiral was perfectly symmetrical, we could do half of one morphology, and rotate/negate as required.

%IM%convert ^
  -size %wi%x%ht% ^
  xc: -draw "point %swi% %sht%" ^
  -mask gr_spiral2.png ^
  -morphology IterativeDistance:-1 Euclidean:2,10 ^
  +mask -fill black -opaque white -auto-level ^
  -draw "point %swi% %sht%" ^
  gr_spiral4.png
gr_spiral4.png
%IM%convert ^
  -size %wi%x%ht% ^
  xc: -draw "point %swi% %sht%" ^
  -mask gr_spiral2n.png ^
  -morphology IterativeDistance:-1 Euclidean:2,10 ^
  +mask -fill black -opaque white -auto-level ^
  -transparent Black ^
  -draw "point %swi% %sht%" ^
  gr_spiral4n.png
gr_spiral4n.png
%IM%convert ^
  ( gr_spiral4.png -negate ) ^
  gr_spiral4n.png ^
  -composite ^
  gr_spiralMask.png
gr_spiralMask.png

Phew. Got there in the end. If this was a final image, we wouldn't want the aliasing. For a video F/X mask, we do, because otherwise we would get weirdness at the edges.


A similar effect is possible, much faster, by compositing the spiral over a radial gradient.

FOR /F "tokens=1,2" %%i ^
IN ('%IM%identify -ping -format "%%w %%h" gr_spiral2.png') ^
DO (
  set wi=%%i
  set ht=%%j
)

%IM%convert ^
  -size %wi%x%wi% ^
  radial-gradient: ^
  gr_spGrad.png

%IM%convert ^
  gr_spGrad.png ^
  ( -clone 0 gr_spiral2.png -alpha off ^
    -compose CopyOpacity -composite ^
  ) ^
  ( -clone 0 -negate ^
    ( gr_spiral2.png -negate -alpha off ) ^
    -compose CopyOpacity -composite ^
  ) ^
  -delete 0 ^
  -compose Over -composite ^
  gr_spFast.png
gr_spFast.png

Rectangles

This mask retains the aspect ratio at all gradations. We create a quarter-mask that can then be appended.

set /A semiWi=%WIDTH%/2
set /A semiHt=%HEIGHT%/2

%IM%convert ^
  ( -size %semiWi%x%semiHt% gradient: -rotate 180 ) ^
  ( -size %semiHt%x%semiWi% gradient: -rotate 90 ) ^
  -compose Darken -composite ^
  gr_semiMask.png
gr_semiMask.png
%IM%convert ^
  gr_semiMask.png ^
  ( +clone -flop ) ^
  +append +repage ^
  ( +clone -flip ) ^
  -append +repage ^
  gr_rectMask.png
gr_rectMask.png
%IM%convert ^
  ( gr_semiMask.png -rotate 180 ) ^
  ( +clone -flop ) ^
  +append +repage ^
  ( +clone -flip ) ^
  -append +repage ^
  gr_crossMask.png
gr_crossMask.png
%IM%convert ^
  -size %WIDTH%X%HEIGHT% ^
  xc:white ^
  -draw ^"polygon %semiWi%,%semiHt% 0,0 %WIDTH%,0 ^" ^
  -draw ^"polygon %semiWi%,%semiHt% 0,%HEIGHT% %WIDTH%,%HEIGHT% ^" ^
  gr_rectM.png
gr_rectM.png
%IM%convert ^
  gr_rectMask.png ^
  ( +clone -negate gr_rectM.png -alpha off -compose Copy-Opacity ) ^
  -compose Over -composite ^
  gr_rectNeg.png
gr_rectNeg.png

Swirl can be used, with care. Posterized to show the effect at transitional corners:

%IM%convert ^
  gr_rectMask.png ^
  -swirl 90 ^
  -posterize 10 ^
  gr_rectMaskS.png
gr_rectMaskS.png

Scripted gradients

Many of the above gradients are (approximately) symmetrical about the centre. This can be changed by suitable cropping, or by a Shepards distortion:

call %PICTBAT%offCentre.bat ^
  gr_spiralMask.png -10 21 gr_spiralOc.png ^
  -filter Point -interpolate Integer
gr_spiralOc.png
call %PICTBAT%mDiamondMask.bat ^
  gr_DiamondMask.png ^
  %WIDTH% %HEIGHT% 6 4
gr_DiamondMask.png
set mbmSEED=1234

call %PICTBAT%mBlobMask.bat ^
  gr_blobMask.png ^
  %WIDTH% %HEIGHT% 6 4 5
gr_blobMask.png
call %PICTBAT%mBlobMask.bat ^
  gr_blobMask2.png ^
  %WIDTH% %HEIGHT%
gr_blobMask2.png
%IM%convert ^
  gr_blobMask2.png ^
  -shade 135x45 ^
  -auto-level ^
  gr_blobMask3.png

set mbmSEED=
gr_blobMask3.png

Transitioning shapes

From an arbitrary black-on-white shape, we can create a gradient mask outwards to the image's edges, or white-on-black for a gradient inwards.

shapeA.png:

shapeA.png
FOR /F %%i ^
IN ('%IM%identify ^
  -format "%%[fx:int(QuantumRange/max(w,h)+0.5)]" ^
  shapeA.png') ^
DO (
  set INC=%%i
)

%IM%convert ^
  shapeA.png ^
  -morphology Distance Euclidean:4,%INC% ^
  -auto-level ^
  -evaluate Multiply 4 ^
  gr_shapeAm.png

rem   -evaluate Multiply 2 ^
gr_shapeAm.png

shapeB.png:

shapeB.png
FOR /F %%i ^
IN ('%IM%identify ^
  -format "%%[fx:int(QuantumRange/max(w,h)*4+0.5)]" ^
  shapeB.png') ^
DO (
  set INC=%%i
)

%IM%convert ^
  shapeB.png ^
  -morphology Distance Euclidean:4,%INC% ^
  -auto-level ^
  gr_shapeBm.png
gr_shapeBm.png

Where are the two fades equal? (Not necessarily 50%, or even a constant level.)

%IM%convert ^
  gr_shapeAm.png ^
  gr_shapeBm.png ^
  -compose Minus -composite ^
  -threshold 1 ^
  gr_shapeThresh.png

Check:

%IM%convert ^
  gr_shapeThresh.png ^
  ( shapeA.png -transparent White -fill Red -opaque Black ) ^
  -composite ^
  ( shapeB.png -transparent White -fill Green -opaque Black ) ^
  -composite ^
  gr_shapeThreshD.png
gr_shapeThresh.png
gr_shapeThreshD.png

Use Am where threshold is white, negated, levelled so darkest is 50%, transparent outwards to the boundary:

%IM%convert ^
  ( gr_shapeAm.png ^
    gr_shapeThresh.png ^
    -compose Multiply -composite ^
    -auto-level -evaluate Divide 2 -negate ^
  ) ^
  gr_shapeThresh.png ^
  -compose CopyOpacity -composite ^
  gr_shapeAml.png
gr_shapeAml.png

Bm where threshold is black, and levelled so lightest is 50%:

%IM%convert ^
  gr_shapeBm.png ^
  ( gr_shapeThresh.png -negate ) ^
  -compose Multiply -composite ^
  -auto-level -evaluate Divide 2 ^
  gr_shapeBml.png
gr_shapeBml.png

Place Aml over Bml; blur to hide the join:

%IM%convert ^
  gr_shapeBml.png ^
  gr_shapeAml.png ^
  -composite ^
  -blur x10 ^
  gr_shapeTrans.png
gr_shapeTrans.png

Alternatively, overlay Am negated over Bm:

%IM%convert ^
  gr_shapeBm.png ^
  ( gr_shapeAm.png -negate ) ^
  -compose Overlay -composite ^
  gr_shapeTrans2.png
gr_shapeTrans2.png

Demonstrate it:

%IM%convert ^
  gr_shapeTrans.png ^
  -posterize 10 ^
  ( shapeA.png -transparent White -fill Red -opaque Black ) ^
  -composite ^
  ( shapeB.png -transparent White -fill Green -opaque Black ) ^
  -composite ^
  gr_shapeTransQ.png
gr_shapeTransQ.png

Demonstrate the alternative:

%IM%convert ^
  gr_shapeTrans2.png ^
  -posterize 10 ^
  ( shapeA.png -transparent White -fill Red -opaque Black ) ^
  -composite ^
  ( shapeB.png -transparent White -fill Green -opaque Black ) ^
  -composite ^
  gr_shapeTrans2Q.png
gr_shapeTrans2Q.png

Another method of transitions between shapes:

Make versions with the transition areas transparent:

%IM%convert ^
  shapeA.png ^
  -transparent White ^
  -fill White -opaque Black ^
  gr_shapeAt.png

%IM%convert ^
  shapeB.png ^
  -transparent White ^
  gr_shapeBt.png
gr_shapeAt.png
gr_shapeBt.png

Make a black-white-gray version:

%IM%convert ^
  ( shapeA.png ^
    -fill rgb(50%%,50%%,50%%) -opaque White ^
    -fill White -opaque Black ) ^
  ( shapeB.png -transparent White ) ^
  -composite ^
  gr_shapeBWG.png
gr_shapeBWG.png

Make a gray version, with the transition area transparent:

%IM%convert ^
  ( shapeA.png ^
    -transparent White ^
    -fill rgb(50%%,50%%,50%%) -opaque Black ^
  ) ^
  ( shapeB.png ^
    -transparent White ^
    -fill rgb(50%%,50%%,50%%) -opaque Black ^
  ) ^
  -composite ^
  gr_shapeG.png
gr_shapeG.png

Make a white-black-gray version.

The blur amount is critical: too small doesn't get a full gradation of grays, too large loses shape.

The gamma gives a more even transition.

%IM%convert ^
  ( shapeA.png ^
    -fill rgb(50%%,50%%,50%%) -opaque White ^
    -fill White -opaque Black ) ^
  ( shapeB.png -transparent White ) ^
  -composite ^
  -blur 0x15 ^
  gr_shapeG.png ^
  -composite ^
  -auto-level ^
  -evaluate Pow 2 ^
  gr_shapeAt.png -composite ^
  gr_shapeBt.png -composite ^
  gr_shapeABgray.png
gr_shapeABgray.png

Demonstrate it:

%IM%convert ^
  gr_shapeABgray.png ^
  -posterize 10 ^
  ( shapeA.png -transparent White -fill Red -opaque Black ) ^
  -composite ^
  ( shapeB.png -transparent White -fill Green -opaque Black ) ^
  -composite ^
  gr_shapeABgrayQ.png
gr_shapeABgrayQ.png

No amount of conventional blur both provides a full gradation of grays and retains detail. So we can use an unconventional blur that does both.

call %PICTBAT%detblur gr_shapeBWG.png gr_shapeBWGd.png 6
gr_shapeBWGd.png
%IM%convert ^
  gr_shapeBWGd.png ^
  gr_shapeG.png ^
  -composite ^
  -auto-level ^
  -sigmoidal-contrast 10x50%% ^
  -evaluate Pow 2.5 ^
  gr_shapeBWGda.png
gr_shapeBWGda.png

Demonstrate it:

%IM%convert ^
  gr_shapeBWGda.png ^
  -posterize 10 ^
  ( shapeA.png -transparent White -fill Red -opaque Black ) ^
  -composite ^
  ( shapeB.png -transparent White -fill Green -opaque Black ) ^
  -composite ^
  gr_shapeBWGdaQ.png
gr_shapeBWGdaQ.png

1D to 2D: making gradients from cluts

A clut ("Colour Look-Up Table") is an image of a one-dimensional array of colours, often representing a gradient. See the Clut Cookbook.

We can transform a clut into a two-dimensional gradient.

Method 1: Make a 1D clut then scale in the other dimension to 2D.

%IM%convert ^
  xc: -bordercolor Black -border 2x0 ^
  -filter gaussian ^
  -resize "%WIDTH%x1^!" ^
  -scale "%WIDTH%x%HEIGHT%^!" ^
  gr_filtgauss.png
gr_filtgauss.png

Method 2: For a symmetrical clut we might want a rotation of half the clut. Make a 1D clut, crop the left half, rotate anti-clockwise, scale it in the other dimension, turn in a polar distortion. The output is square. Suppose we want the output radius to be OUT_RAD, so the output width and height will be 2*OUT_RAD+1.

set OUT_RAD=150
set /A outH=2*%OUT_RAD%
set /A outW=3*%OUT_RAD%

We write gr_filtgauss2.png only for illustration. This isn't normally needed.

%IM%convert ^
  xc: -bordercolor Black -border 2x0 ^
  -filter gaussian ^
  -resize "%outH%x1^!" ^
  -crop %OUT_RAD%x1+0+0 +repage ^
  -rotate -90 ^
  -scale "%outW%x%OUT_RAD%^!" ^
  -write gr_filtgauss2.png ^
  +distort Polar ^"%OUT_RAD%,0 0.5,0.5^" ^
  +repage ^
  gr_filtgauss3.png
gr_filtgauss2.png gr_filtgauss3.png

The pixel colours from the circle (in this case, black) are spread to the corners. "-background Khaki -virtual-pixel HorizontalTile" might seem useful but we get a background colour at the centre pixel. If desired, we can isolate the circle:

%IM%convert ^
  gr_filtgauss3.png ^
  ( +clone ^
    -fill Black -colorize 100 ^
    -fill White -draw "circle %OUT_RAD%,%OUT_RAD% %OUT_RAD%,0" ^
  ) ^
  -alpha off ^
  -compose CopyOpacity -composite ^
  gr_filtgauss3c.png
gr_filtgauss3c.png

Method 3: When we want a rotation of the entire clut, rotated around x=0. As method 2, but we don't crop, and the resize is different.

set OUT_RAD=150
set /A outH=%OUT_RAD%
set /A outW=3*%OUT_RAD%

%IM%convert ^
  xc: -bordercolor Black -border 2x0 ^
  -filter gaussian ^
  -resize "%outH%x1^!" ^
  -rotate -90 ^
  -scale "%outW%x%OUT_RAD%^!" ^
  +distort Polar ^"%OUT_RAD%,0 0.5,0.5^" ^
  +repage ^
  gr_filtgauss4.png
gr_filtgauss4.png

Transforming to colour

"+level-colors" for a simple gradient.

%IM%convert ^
  gr_clock4s.png ^
  +level-colors red,lime ^
  gr_col1.png
gr_col1.pngjpg

"-clut" for a more complexity.

%IM%convert ^
  gr_clock4s.png ^
  ( -size 1x1 ^
    xc:#004 xc:#404 xc:#800 xc:#f00 xc:#f80 xc:#ff0 xc:#ffa ^
    +append ^
  ) ^
  -clut ^
  gr_col2.png

As the input is greyscale, "-hald-clut" would be of no extra benefit.

gr_col2.pngjpg

Greyscale gradients can be combined into channels.

%IM%convert ^
  -size %WIDTH%x%HEIGHT% ^
  gradient: ^
  radial-gradient: ^
  -size %HEIGHT%x%WIDTH% ^
  ( gradient: -rotate 90 ) ^
  -combine ^
  gr_col3.png
gr_col3.pngjpg

The same, but combining in a different colorspace.

%IM%convert ^
  -size %WIDTH%x%HEIGHT% ^
  gradient: ^
  radial-gradient: ^
  -size %HEIGHT%x%WIDTH% ^
  ( gradient: -rotate 90 ) ^
  -set colorspace Lab -combine -colorspace sRGB ^
  gr_col4.png
gr_col4.pngjpg

Miscellaneous

Masks can be combined:

%IM%convert ^
  gr_baseGrad.png ^
  gr_blobMask2.png ^
  -compose Overlay -composite ^
  gr_combMask.png
gr_combMask.png

A mask may be clutted to transform black-white into a number of ramps; in this case 4. "-interpolate nearest-neighbor" ensures we don't get grey anti-aliasing at the black/white boundaries.

%IM%convert -size 1x1000 gradient: ^
  -rotate 90 ^
  -duplicate 3 ^
  +append ^
  gr_ramp_clut.png

%IM%convert -size %WIDTH%x%HEIGHT% radial-gradient: ^
  -interpolate nearest-neighbor ^
  gr_ramp_clut.png -clut ^
  gr_ramp.png
gr_ramp.png

The previous result can give us the three contours at 25% intervals:

%IM%convert ^
  gr_ramp.png ^
  -morphology edgein diamond:1 ^
  gr_rampCont.png
gr_rampCont.png

We might want fill a rectangle with a gradient, with black at top-left and white bottom-right, increasing lightness across then down:

set WW=300
set HH=200
set /A WH=%WW%*%HH%

%IM%convert ^
  -size %WH%x1 ^
  gradient:Black-White ^
  -crop %WW%x ^
  -append ^
  gr_reading.png
gr_reading.png

If we have a white shape on black background, we can make each line of the shape a gradient with black at the left and white at the right. Annoyingly, "-auto-level" doesn't ignore transparent pixels; it merely ignores the alpha channel. So we fill in transparent pixels with the average of each row.

shapeB.png:

shapeB.png
for /F "usebackq" %%L in (`%IM%identify ^
  -format "WW=%%w\nHH=%%h\nWm1=%%[fx:w-1]\nHm1=%%[fx:h-1]" ^
  shapeB.png`) do set %%L

%IM%convert ^
  shapeB.png ^
  ( +clone ^
    -sparse-color Bilinear ^
      "0,0,Black %%[fx:w-1],0,White" ^
  ) ^
  +swap ^
  -alpha off ^
  -compose CopyOpacity -composite ^
  ( +clone ^
    -scale "1x%HH%^!" ^
    -scale "%WW%x%HH%^!" ^
  ) ^
  -compose DstOver -composite ^
  -crop x1 ^
  -auto-level ^
  -append ^
  +repage ^
  gr_sh_grad.png
gr_sh_grad.png

We put this in a script, sh2grad.bat. The script can also rotate the input before processing, and de-rotate afterwards.

The script sh2gradCol.bat calls sh2grad.bat for the red and green channels, setting blue to 50%, and marks pixels outside the shape with black. The result is an absolute displacement map from the rectangle to the shape.

call %PICTBAT%sh2gradCol ^
  shapeB.png gr_sh_grad2.png
gr_sh_grad2.png

The result has many pixels with red==0 and green==0, so these will all be displaced from the top-left corner of the rectangle. Similarly for the other corners.

Scripts

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

greyRectMask.bat

rem Generates a mask width %1 height %2, corners coloured with percentages of grey:
rem   %3 top-left
rem   %4 top-right
rem   %5 bottom-left
rem   %6 bottom-right
rem Width and height must be at least 2.
rem Useful percentages: 0=black, 100=white

"%IM%convert" -size %1x%2 xc: ^
  -sparse-color Bilinear "0,0 rgb(%3%%,%3%%,%3%%) %%[fx:w-1],0 rgb(%4%%,%4%%,%4%%) 0,%%[fx:h-1] rgb(%5%%,%5%%,%5%%) %%[fx:w-1],%%[fx:h-1] rgb(%6%%,%6%%,%6%%)" ^
  greyRectMask.png

ellipMaskCornCol.bat

@%PICTBAT%.ellipMaskCornColbat,h

ellipMaskCorn.bat

rem Generates a grayscale elliptical mask,
rem   width %1 height %2,
rem   centre %3 [default white] fading to %4 [black] at corners.
rem Colours are percentages gray, eg 0=white, 50=mid gray, 100=white
rem The image is contained within the ellipse, so only the corners are the background grey.

set WI=%1

set HT=%2
if "%HT%"=="" set HT=%1

set SIZ=%WI%
if %SIZ% LSS %HT% set SIZ=%HT%

set INCOL=%3
if "%INCOL%"=="" set INCOL=100

set OUTCOL=%4
if "%OUTCOL%"=="" set OUTCOL=0

for /F %%i in ('%IM%convert ^
  xc: ^
  -format "%%[fx:int(%SIZ%*sqrt(2)+0.5)]" ^
  info:') do set SQ_DIM=%%i

%IM%convert -size %SQ_DIM%x%SQ_DIM% ^
  radial-gradient:rgb(%INCOL%%%,%INCOL%%%,%INCOL%%%)-rgb(%OUTCOL%%%,%OUTCOL%%%,%OUTCOL%%%) ^
  -gravity Center ^
  -crop %SIZ%x%SIZ%+0+0 +repage ^
  -resize "%WI%x%HT%^!" ^
  ellipMaskCorn.png

%IM%identify ellipMaskCorn.png

baseMask.bat

rem Generates a mask darker at the bottom, especially the corners,
rem   width %1 height %2,
rem   only dark at bottom %3 percent of image.

call %PICTBAT%ellipMaskCorn %1 %2

"%IM%convert" ^
  -size %1x%2 gradient: -level 0x%3%% ^
  ellipMaskCorn.png ^
  -compose Screen -composite ^
  baseMask.png

offCentre.bat

rem Given image %1 and percentage offset (%2,%3),
rem intended range (-50,-50) to (50,50),
rem creates image %4
rem with centre shifted by the offset.
rem Optional %5 %6 %7 %8 %9: extra parameters for convert.

rem SETLOCAL

FOR /F "tokens=1,2" %%i IN ('%IM%identify -ping -format "%%w %%h" %1') DO (
  set wi=%%i
  set ht=%%j
)

set opts=%5 %6 %7 %8 %9
rem eg rem -filter Point -interpolate Integer

set /A offX=%2
set /A offY=%3

set /A wim1=%wi%-1
set /A htm1=%ht%-1

set /A cx=%wi%/2
set /A cy=%ht%/2

set /A cxpo=%wi%/2+offX*%wi%/100
set /A cypo=%ht%/2+offY*%ht%/100

echo %cxpo%
echo %cypo%

set pinL=0
set pinT=0
set pinR=0
set pinB=0

if %offX% geq 0 set pinL=1
if %offY% geq 0 set pinT=1
if %offX% leq 0 set pinR=1
if %offY% leq 0 set pinB=1

set /A pinTL=%pinT%+%pinL%
set /A pinTR=%pinT%+%pinR%
set /A pinBL=%pinB%+%pinL%
set /A pinBR=%pinB%+%pinR%

echo %pinTL% %pinTR% %pinBL% %pinBR%

rem We pin the 4 corners, the centre-edges, and the edges at cxpo and cypo.

set pin=%cx%,%cy%,%cxpo%,%cypo%

rem Do corners
if %pinTL% gtr 0 set pin=%pin% 0,0,0,0
if %pinTR% gtr 0 set pin=%pin% %wim1%,0,%wim1%,0
if %pinBL% gtr 0 set pin=%pin% 0,%htm1%,0,%htm1%
if %pinBR% gtr 0 set pin=%pin% %wim1%,%htm1%,%wim1%,%htm1%

rem Do edges
if %pinL% gtr 0 set pin=%pin% 0,%cypo%,0,%cypo% 0,%cy%,0,%cy%
if %pinT% gtr 0 set pin=%pin% %cxpo%,0,%cxpo%,0 %cx%,0,%cx%,0
if %pinR% gtr 0 set pin=%pin% %wim1%,%cypo%,%wim1%,%cypo% %wim1%,%cy%,%wim1%,%cy%
if %pinB% gtr 0 set pin=%pin% %cxpo%,%htm1%,%cxpo%,%htm1% %cx%,%htm1%,%cx%,%htm1%

echo %pin%

"%IM%convert" ^
  %1 ^
  %opts% ^
  -distort Shepards ^"%pin%^" ^
  %4

rem ENDLOCAL

mDiamondMask.bat

rem Makes a tiled diamond gradient mask.
rem %1 is output filename.
rem %2 and %3 are overall width and height.
rem %4 and %5 are repetitions horizontally and vertically (default 1).

set outName=%1
if "%outName%"=="" set outName=diamondMask.png

set numWi=%4
if "%numWi%"=="" set numWi=1

set numHt=%5
if "%numHt%"=="" set numHt=1

set /A oneWi=%2/%numWi%
set /A oneHt=%3/%numHt%

echo %oneWi% %oneHt%

set /A outWi=%numWi%*%oneWi%
set /A outHt=%numHt%*%oneHt%

echo %outWi% %outHt%

set /A semiHt=%oneHt%/2
set /A oneHt=%semiHt%*2
set /A semiWi=%oneWi%/2
set /A outHt=%numHt%*%oneHt%

echo %outWi% %outHt%

set /A oneWim1=%oneWi%-1
set /A oneHtm1=%oneHt%-1

"%IM%convert" ^
  -size %oneWi%x%oneHt% ^
  xc:White ^
  -fill Black ^
  -draw ^"polygon %semiWi%,0 %oneWim1%,%semiHt% %semiWi%,%oneHtm1% 0,%semiHt%^" ^
  +repage ^
  %TEMP%\dmMask.png


rem We could use -crop 1x2@ instead of two clones and crops, but we want exact control of the size.

"%IM%convert" ^
  -size %oneWi%x%oneHt% ^
  gradient: ^
  -write %TEMP%\dm0.png ^
  ( -clone 0 -crop %oneWi%x%semiHt%+0+%semiHt% ) ^
  ( -clone 0 -crop %oneWi%x%semiHt%+0+0 ) ^
  -delete 0 ^
  -append ^
  +repage ^
  %TEMP%\dm1.png

"%IM%convert" ^
  %TEMP%\dm0.png ^
  ( %TEMP%\dm1.png -alpha set %TEMP%\dmMask.png -alpha off -compose Copy-Opacity -composite ) ^
  -compose Over -composite ^
  %TEMP%\dmOne.png

rem This starts tiling top-left, so any partial tiles will be at right and bottom.
rem We could use -tile-offset, or generate over-size then trim.

"%IM%convert" ^
  -size %2x%3 ^
  tile:%TEMP%\dmOne.png ^
  %outName%

mBlobMask.bat

rem Makes a tiled blob gradient mask.
rem %1 is output filename.
rem %2 and %3 are overall width and height.
rem %4 and %5 are repetitions horizontally and vertically (default 1).
rem %6 is blur sigma (default 10).
rem %7 0 for greyscale, 1 for colour
@rem
@rem Also uses:
@rem   mbmSEED if given, use this as seed.
@rem
@rem Updated:
@rem   25-May-2016 added mbmSEED feature.


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

@setlocal enabledelayedexpansion

rem @call echoOffSave

call %PICTBAT%setInOut %1 mbm


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

set numWi=%4
if "%numWi%"=="." set numWi=
if "%numWi%"=="" set numWi=1

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

set blurSig=%6
if "%blurSig%"=="." set blurSig=
if "%blurSig%"=="" set blurSig=10

set IsCol=%7
if "%IsCol%"=="." set IsCol=
if "%IsCol%"=="" set IsCol=0

set sSEED=
if not "%mbmSEED%"=="" set sSEED=-seed %mbmSEED%

set TEMP_FILE=%TEMP%\bmOne.miff

set /A oneWi=%2/%numWi%
set /A oneHt=%3/%numHt%

echo %oneWi% %oneHt%

set /A semiHt=%oneHt%/2
set /A oneHt=%semiHt%*2
set /A semiWi=%oneWi%/2

set /A oneWim1=%oneWi%-1
set /A oneHtm1=%oneHt%-1

if %IsCol%==0 (
  set sCOL=-modulate 100,0,100
) else (
  set sCOL=-channel RGB
)

rem This starts tiling top-left, so any partial tiles will be at right and bottom.
rem We could use -tile-offset, or generate over-size then trim.

goto skip
%IM%convert ^
  -size %oneWi%x%oneHt% ^
  xc: ^
  %sSEED% +noise Random ^
  -virtual-pixel Tile ^
  -blur 0,%blurSig% ^
  -modulate 100,0,100 ^
  -auto-level ^
  -auto-gamma ^
  %TEMP_FILE%

%IM%convert ^
  -size %2x%3 ^
  tile:%TEMP_FILE% ^
  %OUTFILE%
:skip

%IM%convert ^
  -size %oneWi%x%oneHt% ^
  xc: ^
  %sSEED% +noise Random ^
  -virtual-pixel Tile ^
  -blur 0,%blurSig% ^
  %sCOL% ^
  -auto-level ^
  -auto-gamma ^
  +channel ^
  +write mpr:NSE +delete ^
  -size %2x%3 ^
  tile:mpr:NSE ^
  %OUTFILE%

call echoRestore

@endlocal & set mbmOUTFILE=%OUTFILE%

detblur.bat

rem %1 is input image file
rem %2 is output image file
rem %3 is number of iterations

setlocal ENABLEDELAYEDEXPANSION

set INFILE=%1

set OUTFILE=%2

set NITER=%3
if "%NITER%"=="" set NITER=2

if /I %NITER% LSS 1 goto bad

set /A nClone=0
set /A nBlur=1

set strDB=

for /L %%i in (1,1,%NITER%) do (
  set strDB=!strDB! ^( -clone !nClone! -gaussian-blur x!nBlur! -auto-level ^)
  set /A nClone+=1
  set /A nBlur*=2
)

echo %strDB%

%IM%convert ^
  %1^
  %strDB% ^
  -evaluate-sequence Mean ^
  +depth ^
  %2

goto :eof

:bad
echo Call with: infile outfile niter

sh2grad.bat

rem Given %1 is white shape on black background,
rem replaces shape with each line gradient
rem with black at left of shape, white at right of shape.
rem %2 output.
rem %3 rotate degrees clockwise before processing (and de-rotate after).


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

@setlocal enabledelayedexpansion

@call echoOffSave

call %PICTBAT%setInOut %1 s2g

if not "%2"=="" set OUTFILE=%2

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

if %nROT%==0 (
  set IN_ROT=
  set OUT_ROT=
) else (
  set IN_ROT=-rotate %nROT% +repage
  set OUT_ROT=-rotate -%nROT% +repage
  set OUT_ROT=!OUT_ROT:--=!
)

set TMP_IMG=s2g_tmp.miff

%IM%convert ^
  %INFILE% ^
  -background Black ^
  %IN_ROT% ^
  +depth ^
  %TMP_IMG%

for /F "usebackq" %%L in (`%IM%identify ^
  -format "WW=%%w\nHH=%%h" ^
  %TMP_IMG%`) do set %%L

%IM%convert ^
  %TMP_IMG%^
  ( +clone ^
    -sparse-color Bilinear ^
      "0,0,Black %%[fx:w-1],0,White" ^
  ) ^
  +swap ^
  -alpha off ^
  -compose CopyOpacity -composite ^
+write x0.png ^
  ( +clone ^
    -scale "1x%HH%^!" ^
    -scale "%WW%x%HH%^!" ^
    -alpha Opaque ^
+write x0a.png ^
  ) ^
  -compose DstOver -composite ^
+write x1.png ^
  -crop x1 ^
  -auto-level ^
  -append ^
+write x2.png ^
  +repage ^
  -background Black ^
  %OUT_ROT% ^
  %OUTFILE%

call echoRestore

@endlocal & set s2gOUTFILE=%OUTFILE%

sh2gradCol.bat

rem Given %1 is white shape on black background,
rem replaces shape with each absolute displacement map
rem from rectangle to shape.
rem %2 output.

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

@setlocal enabledelayedexpansion

@call echoOffSave

call %PICTBAT%setInOut %1 s2gc

if not "%2"=="" set OUTFILE=%2

call %PICTBAT%sh2grad %INFILE% s2gc_red.miff
if ERRORLEVEL 1 exit /B 1

call %PICTBAT%sh2grad %INFILE% s2gc_grn.miff -90
if ERRORLEVEL 1 exit /B 1

%IM%convert ^
  s2gc_red.miff ^
  s2gc_grn.miff ^
  ( +clone -fill gray(50%%) -colorize 100 ) ^
  -combine ^
  %INFILE% ^
  -alpha off ^
  -compose CopyOpacity -composite ^
  -background Black -compose Over -layers Flatten ^
  %OUTFILE%
if ERRORLEVEL 1 exit /B 1

call echoRestore

@endlocal & set s2gcOUTFILE=%OUTFILE%

slopeXYdirn.bat

rem Given %1 is slopeXY structure (thus contains 2 images),
rem returns %2 conventional RGB image
rem with each channel set to direction.
rem %3 if SWAP, swap the two inputs.
rem %4 if SUB, subtract 50% before taking the arctan.
rem %5 percentage for addmodulus.

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

@setlocal

@call echoOffSave

call %PICTBAT%setInOut %1 sxyd

if not "%2"=="" set OUTFILE=%2

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

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

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

if /I "%SWAP%"=="SWAP" (
  set sSWAP=+swap
) else (
  set sSWAP=
)

if /I "%SUBHALF%"=="SUB" (
  set sSUBHALF=-evaluate Subtract 50%%
) else (
  set sSUBHALF=
)

echo %0: sSWAP=%sSWAP% sSUBHALF=%sSUBHALF% nADDMOD=%nADDMOD%

%IMDEV%convert ^
  %INFILE% ^
  %sSWAP% ^
  %sSUBHALF% ^
  -process 'arctan2 b 0' ^
  -evaluate AddModulus %nADDMOD%%% ^
  +depth ^
  -depth 32 ^
  -define quantum:format=floating-point ^
  %OUTFILE%

if ERRORLEVEL 1 exit /B 1

call echoRestore

endlocal & set sxydOUTFILE=%OUTFILE%

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

%IM%identify -version
Version: ImageMagick 6.9.5-3 Q16 x86 2016-07-22 http://www.imagemagick.org
Copyright: Copyright (C) 1999-2015 ImageMagick Studio LLC
License: http://www.imagemagick.org/script/license.php
Visual C++: 180040629
Features: Cipher DPC Modules OpenMP 
Delegates (built-in): bzlib cairo flif freetype jng jp2 jpeg lcms lqr openexr pangocairo png ps rsvg tiff webp xml zlib

Source file for this web page is gradients.h1. To re-create this web page, execute gradients.bat.


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 v2.1 6-Jan-2014.

Page created 09-Jul-2017 13:09:57.

Copyright © 2017 Alan Gibson.