snibgo's ImageMagick pages

Membranes

blah

A physical membrane might be a thin sheet of rubber stretched across a wire loop. If the points on the wire loop are co-planar, the membrane will be in the same plane. But if the points on the wire are not co-planar, the membrane will take a complex 3-D shape.

Membranes are sometimes useful in image processing, such as for seamless pasting of one image over another.

This can be regarded as a hole-filling technique, where the hole is filled using colour informaton only from pixels at the edge of the hole. (See Filling holes for other techniques.)

Code on this page uses a process module, midlightest.

Sample input

We create source images to demonstrate techniques:

is_easy_mainland.png

for /F "usebackq" %%L in (`%IM%identify ^
  -format "WW=%%w\nHH=%%h\nWW_2=%%[fx:w/2]\nHH_2=%%[fx:h/2]\nDIAG_2=%%[fx:hypot(w,h)/2]" ^
  is_easy_mainland.png`) do set %%L

echo WW=%WW%  HH=%HH%  DIAG_2=%DIAG_2% 
WW=320  HH=200  DIAG_2=188.68 
is_easy_mainland.png
set mbmSEED=1234

call %PICTBAT%mBlobMask mem_full.png %WW% %HH%

%IM%convert ^
  -size %HH_2%x%WW% gradient: ^
  -function Sinusoid 2,90 -rotate 90 ^
  +distort Polar "%HH_2%,0 -.5,0.5" ^
  -resize "%WW%x%HH%^!" ^
  +repage ^
  mem_full.png ^
  -evaluate-sequence Mean ^
  mem_full.png
mem_full.pngjpg
%IM%convert ^
  mem_full.png ^
  ( is_easy_mainland.png -alpha off -negate ) ^
  -compose CopyOpacity -composite ^
  -background gray(50%%) -alpha background ^
  -auto-level ^
  mem_hollow.png
mem_hollow.png
%IM%convert ^
  mem_hollow.png ^
  ( is_easy_mainland.png -morphology EdgeOut Square ) ^
  -compose CopyOpacity -composite ^
  -background Black -alpha background ^
  mem_rim.png
mem_rim.png

(EdgeOut with square, instead of diamond, gives a 4-connected rim. This reduces the need for super-sampling when we unroll.)

mem_hollow.png and mem_rim.png have transparent centres. We will populate these. An application may be concerned with the equivalent of either mem_hollow.png or mem_rim.png

We can consider the grayscale to be a heightfield, where light pixels are higher than low pixels. We stretch a membrane, like a thin sheet of rubber, across the transparent centre, fixing it to the heights of the pixels of the rim. The height at each point on the membrane define the pixel values.

The height of the membrane will be influenced by the height of the rim, but not by points outside the rim.

There are mathematical techniques for membranes that could be coded in C. The method shown here is different to a lowest-energy membrane: the gradient will be constant from any rim point to the centre, but the gradient will change suddenly (be discontinuous) at the centre.

We will initally consider grayscale only. Colour images, with three channels, can have the same processing applied to each channel independently.

Blur fill

A simple and obvious technique for creating a membrane is to repeatedly blur the rim.

call %PICTBAT%blurFillSparse mem_rim.png . mem_bfs.png

call %PICTBAT%blurFill mem_bfs.png . mem_bfs.png
mem_bfs.png

The result isn't too bad, but there is obvious discontinuity in tone at the centre. This is inevitable, when blurs from opposing sides of the rim meet in the middle.

Instead, an alternative is to calculate a required value for the centre, and ensure that all values from the rim inwards work towards this value.

A centre value

What should be the gray value in the centre of the image? An approximation would be the average of the values around the rim. We can easily calculate this, on a scale of 0.0 (black) to 1.0 (white):

%IM%convert mem_rim.png -scale "1x1^!" -format "%%[fx:mean]" info: 
0.526787

This takes the average of the rim values, giving equal weighting to all rim points, however far they are from the centre. A better value will come from a weighted average, where near points have a greater weight than distant points. This is considered below.

The method

This method finds coordinates (CX,CY) of the centre of the rim line, and calculates Vc, the required value of the membrane at that point. Then it populates the pixel values for the membrane, such that:

Gradients

We can define a gradient within the rim, to a centre that we define as the point that is furthest from any rim point. Assuming the rim is within the image, the maximum possible distance between the rim and this centre is the semi-diagonal of the image.

This worked example uses is_easy_mainland.png to define the inside of the rim. The script will derive this data from the rim image.

The next two commands must use IM varieties with the same Q-number.

for /F "usebackq" %%L in (`%IMDEV%identify ^
  -precision 19 ^
  -format "DELTA=%%[fx:int(QuantumRange/%DIAG_2%)]" ^
  mem_rim.png`) do set %%L

echo DELTA=%DELTA% 
DELTA=22763235 
for /F "usebackq tokens=1-2 delims=," %%X in (`%IMDEV%convert ^
  is_easy_mainland.png ^
  -morphology Distance "Euclidean:4,%DELTA%" ^
  +depth ^
  +write mem_rimgrad.png ^
  -process midlightest ^
  NULL: 2^>^&1`) do (
  set CX=%%X
  set CY=%%Y
)

echo CX=%CX%  CY=%CY% 
CX=154  CY=101 
mem_rimgrad.png
%IM%convert ^
  -size %WW%x%HH% ^
  -define gradient:center=%CX%,%CY% ^
  -define gradient:radii=%DIAG_2%,%DIAG_2% ^
  radial-gradient: ^
  mem_radgrad.png
mem_radgrad.png
call %PICTBAT%fanComp ^
  mem_radgrad.png ^
  mem_rimgrad.png ^
  mem_fangrad.png
mem_fangrad.png

The result, mem_fangrad.png, is white at this defined centre, and black at the rim and outside the rim.

ASIDE: Just for interest, we find the contours of the mem_fangrad.png gradient:

%IM%convert ^
  mem_fangrad.png ^
  ( -size 1x500 gradient: -rotate 90 -duplicate 10 +append ) ^
  -clut ^
  -morphology edgein diamond:1 ^
  mem_fgc.png
mem_fgc.png

Calculate Vc

The mem_radgrad.png gradient, negated, gives the distance from the centre (CX,CY) to any point, on a scale of 0.0 to 1.0, where 1.0 is the semi-diagonal of the image. We can mask this with the transparency from mem_im.png to get the distance to each point on the rim.

%IM%convert ^
  mem_radgrad.png ^
  -negate ^
  mem_rim.png ^
  -compose copy_opacity -composite ^
  mem_rdist.png
mem_rdist.png

To get Vc, the gray value at the centre (CX,CY), we find the weighted average rim values:

      sum(Vr.Dr)
Vc = -----------
       sum(Dr)

... where Vr is the value at a rim point and Dr is the distance from the centre to that rim point. We can divide top and bottom by n, where n is the number of rim points, so:

for /F "usebackq" %%L in (`%IM%convert ^
  mem_rim.png ^
  mem_rdist.png ^
  -compose Multiply -composite ^
  -scale "1x1^!" ^
  -format "SUM_VR_DR=%%[fx:mean.r]" ^
  info:`) do set %%L

for /F "usebackq" %%L in (`%IM%convert ^
  mem_rdist.png ^
  -scale "1x1^!" ^
  -format "SUM_DR=%%[fx:mean.r]" ^
  info:`) do set %%L

for /F "usebackq" %%L in (`%IM%identify ^
  -format "Vc=%%[fx:%SUM_VR_DR%/%SUM_DR%]\nVcPC=%%[fx:100*%SUM_VR_DR%/%SUM_DR%]" ^
  xc:`) do set %%L

echo SUM_VR_DR=%SUM_VR_DR% SUM_VR_DR=%SUM_DR% Vc=%Vc% 
SUM_VR_DR=0.323705 SUM_VR_DR=0.658473 Vc=0.4916 

Vc is the value at the centre (CX,CY). If the entire membrane was at this value, it would look like this:

%IM%convert ^
  -size %WW%x%HH% ^
  xc:gray(%VcPC%%%) ^
  mem_vc.png
mem_vc.png

Make the membrane

Now we know Vc, the value at the centre, and the value at each rim point, Vr. To get a linear gradient between these two, we want the value Vp at any point P between the centre and a rim point to be:

Vp = Gp.Vc + (1-Gp).Vr
   = -Gp.Vr + Gp.Vc + Vr

... where Gp is the gradient at point P, from mem_fangrad.png, scaled from 0.0 (at the centre) to 1.0. Vc is a scalar constant, derived above.

Before we can calculate Vp, we need to know the rim value Vr required for point each P. We find this by unrolling the rim around (CX,CY), spreading the values vertically with shiftFill.bat, and rolling that back up.

We create and show small versions of intermediate results purely for illustration.

Unroll the rim.

%IM%convert ^
  mem_rim.png ^
  -distort depolar -1,0 ^
  ( +clone -alpha extract -threshold 50%% ) ^
  -alpha off ^
  -compose CopyOpacity -composite ^
  +write mem_rim_spd.png ^
  -resize "%WW%x%HH%^!" ^
  mem_rim_spd_sm.png
mem_rim_spd_sm.png

Spread values vertically.

call %PICTBAT%shiftFill ^
  mem_rim_spd.png 0x mem_rim_shft.png

%IM%convert ^
  mem_rim_shft.png ^
  -resize "%WW%x%HH%^!" ^
  mem_rim_shft_sm.png
mem_rim_shft_sm.png

Roll it back up.

%IM%convert ^
  mem_rim_shft.png ^
  -distort polar -1,0 ^
  -resize "%WW%x%HH%^!" ^
  mem_rim_spd2.png
mem_rim_spd2.png

If the rim was thinner, we might need "-resize 200%" or "-resize 400%" before "-distort depolar". Even then, some values in the rim, where they lie on a radius from (Cx,Cy), might be skipped (made transparent). The spreading by shiftFill would fill those in.

The image mem_rim_spd2.png contains the values for Vr, the rim values spread along the radii from (CX,CY).

Now we calculate the membrane values, Vp = -Gp.Vr + Gp.Vc + Vr

%IM%convert ^
  mem_fangrad.png ^
  mem_rim_spd2.png ^
  -compose Mathematics ^
    -define compose:args=-1,1,%Vc%,0 -composite ^
  mem_mem.png
mem_mem.pngjpg

The image mem_mem.png is the required membrane, with values extrapolated outside the rim. We can check it by compositing mem_hollow.png over it:

%IM%convert ^
  mem_mem.png ^
  mem_hollow.png ^
  -composite ^
  mem_mem_chk.png
mem_mem_chk.pngjpg

Modulate the membrane

If we want, we can use mem_fangrad.png as a mask to modulate the effect. This will steepen the slope near the rim while zeroing the slope near the centre, resulting in continuity of slope here.

This is useful provided the rim doesn't have a tilt. Any modulation reduces contrast, thus introduces flatness, in some areas. If the rim has a systemic tilt (for example, an oblique cross-section of a cone), this will look wrong. An alternative is to introduce a horizontal blur after the spreading of the unrolled vales; see Selective blur.

%IM%convert ^
  mem_mem.png ^
  mem_vc.png ^
  mem_fangrad.png ^
  -composite ^
  mem_mem_mod1.png
mem_mem_mod1.pngjpg

We can tweak the mask, usually such that 0.0 and 1.0 remain unchanged, but other values are changed, eg by -evaluate Pow or -sigmoidal-contrast.

%IM%convert ^
  mem_mem.png ^
  mem_vc.png ^
  ( mem_fangrad.png -sigmoidal-contrast 10,50%% ) ^
  -composite ^
  mem_mem_mod2.png
mem_mem_mod2.pngjpg

A more direct technique for modulating the result is to tweak values of Gp:

%IM%convert ^
  mem_fangrad.png -sigmoidal-contrast 10,50%% ^
  mem_rim_spd2.png ^
  -compose mathematics ^
    -define compose:args=-1,1,%Vc%,0 -composite ^
  mem_mem_mod3.png
mem_mem_mod3.pngjpg

Relaxing

We can relax any of the above results. (See Filling holes: relaxation.) First, we set some abbreviations to simplify the commands.

set FREQm1=49

set TO_CONTOUR=^
( -size 1x500 gradient: -rotate 90 ^
-duplicate %FREQm1% +append ) ^
-clut -morphology edgein diamond:1 -threshold 40%% ^
mem_hollow.png -composite

set RF_PARAMS=1e-6 50

The following table shows the source used for the first approximation, the result from relaxation, and a contour of that result.

Code Source Relaxed Contour of relaxed
call %PICTBAT%relaxFill ^
  mem_hollow.png mem_bfs.png ^
  mem_bfs_rf.png %RF_PARAMS%

echo rfITER=%rfITER% 
rfITER=6700 
%IM%convert ^
  mem_bfs_rf.png ^
  %TO_CONTOUR% ^
  mem_bfs_c.png
mem_bfs.pngjpg mem_bfs_rf.pngjpg mem_bfs_c.pngjpg
call %PICTBAT%relaxFill ^
  mem_hollow.png mem_mem.png ^
  mem_mem_rf.png %RF_PARAMS%

echo rfITER=%rfITER% 
rfITER=5050 
%IM%convert ^
  mem_mem_rf.png ^
  %TO_CONTOUR% ^
  mem_mem_c.png
mem_mem.pngjpg mem_mem_rf.pngjpg mem_mem_c.pngjpg
call %PICTBAT%relaxFill ^
  mem_hollow.png mem_mem_mod1.png ^
  mem_mem_mod1_rf.png %RF_PARAMS%

echo rfITER=%rfITER% 
rfITER=3050 
%IM%convert ^
  mem_mem_mod1_rf.png ^
  %TO_CONTOUR% ^
  mem_mem_mod1_c.png
mem_mem_mod1.pngjpg mem_mem_mod1_rf.pngjpg mem_mem_mod1_c.pngjpg
call %PICTBAT%relaxFill ^
  mem_hollow.png mem_mem_mod2.png ^
  mem_mem_mod2_rf.png %RF_PARAMS%

echo rfITER=%rfITER% 
rfITER=3550 
%IM%convert ^
  mem_mem_mod2_rf.png ^
  %TO_CONTOUR% ^
  mem_mem_mod2_c.png
mem_mem_mod2.pngjpg mem_mem_mod2_rf.pngjpg mem_mem_mod2_c.pngjpg
call %PICTBAT%relaxFill ^
  mem_hollow.png mem_mem_mod3.png ^
  mem_mem_mod3_rf.png %RF_PARAMS%

echo rfITER=%rfITER% 
rfITER=4150 
%IM%convert ^
  mem_mem_mod3_rf.png ^
  %TO_CONTOUR% ^
  mem_mem_mod3_c.png
mem_mem_mod3.pngjpg mem_mem_mod3_rf.pngjpg mem_mem_mod3_c.pngjpg

Without supplying a first approximation:

call %PICTBAT%relaxFill ^
  mem_hollow.png . ^
  mem_none_rf.png %RF_PARAMS%

echo rfITER=%rfITER% 
rfITER=5300 
%IM%convert ^
  mem_none_rf.png ^
  %TO_CONTOUR% ^
  mem_none_c.png

[No image]

mem_none_rf.pngjpg mem_none_c.pngjpg

The results are similar. The contour shows the saddle we expect, as the top and bottom are lighter than the left and right sides. The blur-fill source took the least number of iterations to stabilise, but it is slightly different to the other results.

%IM%compare -metric RMSE mem_none_rf.png mem_bfs_rf.png NULL: 
3077.19 (0.0469549)

The other results are practically identical to each other, for example:

%IM%compare -metric RMSE mem_none_rf.png mem_mem_rf.png NULL: 
1008.79 (0.0153931)

For good performance, we should generally use the multi-scale version of the script, relaxFillMS.bat. For this, there is virtually no benefit from supplying a first approximation, so we don't.

Code Relaxed Contour of relaxed
call %PICTBAT%relaxFillMS ^
  mem_hollow.png . ^
  mem_none_rfms.png %RF_PARAMS%

echo rfmsITER=%rfmsITER% 
rfmsITER=3500 
%IM%convert ^
  mem_none_rfms.png ^
  %TO_CONTOUR% ^
  mem_none_msc.png
mem_none_rfms.pngjpg mem_none_msc.pngjpg
%IM%compare -metric RMSE mem_none_rf.png mem_none_rfms.png NULL: 
1861.08 (0.0283983)

relaxFillMS.bat gives virtually the same result as relaxFill.bat but with far fewer iterations (about 3%), and most of those iterations are at coarse scale (i.e. on smaller images).

The script

The script membrane.bat performs the above operations (but currently without relaxation). It operates on three channels independently.

call %PICTBAT%membrane mem_rim.png mem_out2.png
mem_out2.png

We make a colour rim:

set mbmSEED=1234

call %PICTBAT%mBlobMask mem_full_c.png %WW% %HH% . . . 1

%IM%convert ^
  -size %HH_2%x%WW% gradient: ^
  -function Sinusoid 2,90 -rotate 90 ^
  +distort Polar "%HH_2%,0 -.5,0.5" ^
  -resize "%WW%x%HH%^!" ^
  +repage ^
  mem_full_c.png ^
  -evaluate-sequence Mean ^
  mem_full_c.png
mem_full_c.pngjpg
%IM%convert ^
  mem_full_c.png ^
  ( is_easy_mainland.png -alpha off -negate ) ^
  -compose CopyOpacity -composite ^
  -background gray(50%%) -alpha background ^
  -auto-level ^
  mem_hollow_c.png
mem_hollow_c.png
%IM%convert ^
  mem_hollow_c.png ^
  ( is_easy_mainland.png -morphology EdgeOut Square ) ^
  -compose CopyOpacity -composite ^
  -background Black -alpha background ^
  mem_rim_c.png
mem_rim_c.png

From that colour rim, calculate a membrane:

call %PICTBAT%membrane ^
  mem_rim_c.png mem_out_c.png
mem_out_c.pngjpg

Future

Scripts

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

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%

membrane.bat

rem From %1, a image where pixels on a rim are opaque and others are transparent,
rem write %2, a membrane image.
@rem
@rem Also uses:
@rem   memSUPER_SAMP_PC percentage for supersampling, eg 400.
@rem
@rem Also sets environment vaiables:
@rem   memCX, memCY  coordinates of rim centre.
@rem   memVc  colour at (memCX,memCY).
@rem

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

@setlocal enabledelayedexpansion

@call echoOffSave

call %PICTBAT%setInOut %1 mem

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

set memSUPER_SAMP_PC=




set EXT=.miff
set RIMGRAD=%BASENAME%_mem_rimgrad%EXT%
set RADGRAD=%BASENAME%_mem_radgrad%EXT%
set FANGRAD=%BASENAME%_mem_fangrad%EXT%
set RDIST=%BASENAME%_mem_rdist%EXT%
set RIMSPD=%BASENAME%_mem_rimspd%EXT%
set FCPIX=%BASENAME%_mem_fcpix%EXT%

set WW=
for /F "usebackq" %%L in (`%IM%identify ^
  -format "WW=%%w\nHH=%%h\nWW_2=%%[fx:w/2]\nHH_2=%%[fx:h/2]\nDIAG_2=%%[fx:hypot(w,h)/2]" ^
  %INFILE%`) do set %%L

if "%WW%"=="" exit /B 1

:: Next two converts must use same Q-number.

for /F "usebackq" %%L in (`%IMDEV%identify ^
  -precision 19 ^
  -format "DELTA=%%[fx:int(QuantumRange/%DIAG_2%)]" ^
  %INFILE%`) do set %%L

echo %0: WW=%WW% HH=%HH% DIAG_2=%DIAG_2% DELTA=%DELTA% 

for /F "usebackq tokens=1-2 delims=," %%X in (`%IMDEV%convert ^
  %INFILE% ^
  -alpha extract ^
  -bordercolor Black -border 1 ^
  -fill Red -draw "color 0,0 floodfill" ^
  -shave 1x1 ^
  -fill White -opaque Red ^
  -negate ^
  -morphology Distance "Euclidean:4,%DELTA%" ^
  +depth ^
  +write %RIMGRAD% ^
  -process midlightest ^
  NULL: 2^>^&1`) do (
  set CX=%%X
  set CY=%%Y
)

echo %0: CX=%CX%  CY=%CY%

%IM%convert ^
  -size %WW%x%HH% ^
  -define gradient:center=%CX%,%CY% ^
  -define gradient:radii=%DIAG_2%,%DIAG_2% ^
  radial-gradient: ^
  %RADGRAD%

call %PICTBAT%fanComp ^
  %RADGRAD% ^
  %RIMGRAD% ^
  %FANGRAD%
if ERRORLEVEL 1 exit /B 1

echo %0: Made fancomp.

%IM%convert ^
  %RADGRAD% ^
  -negate ^
  %INFILE% ^
  -compose copy_opacity -composite ^
  +write mpr:RDIST ^
  %INFILE% ^
  -compose Multiply -composite ^
  mpr:RDIST ^
  -scale "1x1^!" ^
  -alpha off ^
  -compose DivideSrc -composite ^
  %FCPIX%

echo %0: Made fcpix.

if "%memSUPER_SAMP_PC%"=="" (
  set sRES=
) else (
  set sRES=-resize %memSUPER_SAMP_PC%%%
)


%IM%convert ^
  %INFILE% ^
  %sRES% ^
  -distort depolar -1,0 ^
  ( +clone -alpha extract -threshold 50%% ) ^
  -alpha off ^
  -compose CopyOpacity -composite ^
  %RIMSPD%

echo %0: Made rimspd.

call %PICTBAT%shiftFill %RIMSPD% 0x %RIMSPD%
if ERRORLEVEL 1 exit /B 1

echo %0: done shiftFill.

%IM%convert ^
  %RIMSPD% ^
  -distort polar -1,0 ^
  -resize "%WW%x%HH%^!" ^
  %RIMSPD%


:: out = rimspd - (fan * rimspd) + (fan * fcpix)

%IMDEV%convert ^
  %FANGRAD% +write mpr:FAN ^
  ( %FCPIX% -scale "%WW%x%HH%^!" +write mpr:FC ) ^
  -compose Multiply -composite ^
  +write mpr:FxGC ^
  -delete 0-2 ^
  %RIMSPD% +write mpr:SPD ^
  ( mpr:FAN mpr:SPD -compose Multiply -composite -negate ) ^
  mpr:FxGC ^
  -evaluate-sequence AddModulus ^
  mpr:FC ^
  mpr:FAN ^
  -compose Over -composite ^
  %OUTFILE%

echo %0: Made outfile.


call echoRestore

endlocal & set memOUTFILE=%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

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 membrane.h1. To re-create this web page, run "procH1 membrane".


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 17-June-2016.

Page created 30-Oct-2016 20:38:22.

Copyright © 2016 Alan Gibson.