snibgo's ImageMagick pages

Gradient contours

A grayscale gradient image can be made more readable by showing contour lines, or lines perpendicular to contours.

A contour connects adjacent pixels that have the same value. They are very easy to construct. If the image represents a landscape with lightness representing heights, then contour lines would look like those on a map.

With more difficulty, we can draw lines perpendicular to the contours. These represent paths water would take flowing down the hilly landscape.

Commands and scripts on this page use my "cumulhisto" process module. For source code and installation instructions, see Process modules: cumulhisto.

Sample inputs

Make an image with smoothly changing gray values.

set GRC_SRC=grc_src1.png


%IM%convert ^
  -size 600x400 xc: ^
  -sparse-color bilinear ^
    0,0,gray30,599,0,gray33,0,399,black,599,399,white ^
  %GRC_SRC%
grc_src1.png

Simple contours

We can draw contours that follow lines of constant tone. These are like contours on a map that join points of the same height.

set FREQm1=49

%IM%convert ^
  %GRC_SRC% ^
  ( -size 1x500 gradient: -rotate 90 ^
    -duplicate %FREQm1% +append ) ^
  -clut ^
  -morphology edgein diamond:1 ^
  -threshold 40%% ^
  grc_src1_sc.png
grc_src1_sc.png

Contour lines are close together where the tone changes rapidly; the gradient is steep. They are far apart where the tone changes slowly; the gradient is shallow.

The contour scale is constant. There is one contour line per 1/50th of the interval between black and white.

The result is aliased (jagged), even before the threshold.

We can apply the same technique to photographs:

%IM%convert ^
  toes.png ^
  ( -size 1x500 gradient: -rotate 90 ^
    -duplicate 7 +append ) ^
  -clut ^
  -morphology edgein diamond:1 ^
  -threshold 40%% ^
  grc_toes_sc.png

%IM%convert ^
  toes.png ^
  ( grc_toes_sc.png ^
    -alpha set ^
    -channel alpha ^
    -evaluate set 25%% ^
    +channel ^
  ) ^
  -composite ^
  grc_toes_sc_o.png
grc_toes_sc.png grc_toes_sc_o.pngjpg

Variable-scale contours

We can vary the contour scale. First, create a gradient image. The auto-levelled version is purely for display, not for further processing.

%IM%convert ^
  %GRC_SRC% ^
  -statistic Gradient 10x10 ^
  +write grc_s1_gr.png ^
  -format "min=%%[fx:minima] max=%%[fx:maxima]\n" ^
  +write info: ^
  -auto-level ^
  grc_s1_gr_al.png 
min=0.000549325 max=0.0298161
grc_s1_gr_al.png

The maximum gradient across a 10x10 square is about 3%. If the frequency was 33, we would have an interval of about 10 pixels between contours.

To get the same interval of 10 pixels/contour, a 2% gradient would need a frequency of 50, and a 1% gradient would need a frequency of 100.

We can partition the image into areas where the gradient is closest to 0%, 1%, 2% or 4%. We do this by mapping to a 4x1 image with those percentages of gray, and creating one mask per area.

The script mMapMasks.bat makes N map masks from an image and a Nx1 map. Each mask is the same size as the image. The i-th mask is white where the image pixel is closer to the i-th pixel in a map than to other pixels in the map.

The script also creates cumulative masks. The i-th cumulative mask is white where the image pixel is closer to the i-th or lower pixel in a map than to higher pixels in the map. There are (N-1) cumulative masks.

%IM%convert ^
  xc:black xc:gray(1%%) xc:gray(2%%) xc:gray(4%%) +append ^
  grc_map1.png

call %PICTBAT%mMapMasks grc_s1_gr.png grc_map1.png grc_s1_msk_.png

The four masks are:

grc_s1_msk_1.png grc_s1_msk_2.png grc_s1_msk_3.png grc_s1_msk_4.png

We can verify that these masks give exact coverage with no overlap. The mean of all the masks should be a single colour.

%IM%convert ^
  grc_s1_msk*.png ^
  -evaluate-sequence Mean ^
  -unique-colors ^
  txt: 
# ImageMagick pixel enumeration: 3,1,65535,gray
0,0: (18724,18724,18724)  #494949  gray(73)
1,0: (28086,28086,28086)  #6D6D6D  gray(109)
2,0: (37449,37449,37449)  #929292  gray(146)

The three cumulative masks are:

grc_s1_msk_c_2.png grc_s1_msk_c_3.png grc_s1_msk_c_4.png

The second is white except for a small black triangle in the bottom-right corner. The third is entirely white.

We create four images with the required contour frequencies:

set FREQm1=199

%IM%convert ^
  %GRC_SRC% ^
  ( -size 1x500 gradient: -rotate 90 ^
    -duplicate %FREQm1% +append ) ^
  -clut ^
  -morphology edgein diamond:1 ^
  -threshold 40%% ^
  grc_s1_f1.png
grc_s1_f1.png
set FREQm1=99

%IM%convert ^
  %GRC_SRC% ^
  ( -size 1x500 gradient: -rotate 90 ^
    -duplicate %FREQm1% +append ) ^
  -clut ^
  -morphology edgein diamond:1 ^
  -threshold 40%% ^
  grc_s1_f2.png
grc_s1_f2.png
set FREQm1=49

%IM%convert ^
  %GRC_SRC% ^
  ( -size 1x500 gradient: -rotate 90 ^
    -duplicate %FREQm1% +append ) ^
  -clut ^
  -morphology edgein diamond:1 ^
  -threshold 40%% ^
  grc_s1_f3.png
grc_s1_f3.png
set FREQm1=24

%IM%convert ^
  %GRC_SRC% ^
  ( -size 1x500 gradient: -rotate 90 ^
    -duplicate %FREQm1% +append ) ^
  -clut ^
  -morphology edgein diamond:1 ^
  -threshold 40%% ^
  grc_s1_f4.png
grc_s1_f4.png

We create a merged image from the contour images and masks.

%IM%convert ^
  grc_s1_f1.png ^
  grc_s1_f2.png ^
  grc_s1_msk_2.png ^
  -composite ^
  grc_s1_f3.png ^
  grc_s1_msk_3.png ^
  -composite ^
  grc_s1_f4.png ^
  grc_s1_msk_4.png ^
  -composite ^
  grc_s1_mrgd.png
grc_s1_mrgd.png

Note that mask grc_s1_msk_1.png isn't used.

Note that doubling frequency gives continuous contours.

The contours are white so we can easily colour them:

%IM%convert ^
  ( grc_s1_f1.png -fill #a00 -opaque #fff ) ^
  ( grc_s1_f2.png -fill #f00 -opaque #fff ) ^
  grc_s1_msk_2.png ^
  -composite ^
  ( grc_s1_f3.png -fill #f80 -opaque #fff ) ^
  grc_s1_msk_3.png ^
  -composite ^
  ( grc_s1_f4.png -fill #ff0 -opaque #fff ) ^
  grc_s1_msk_4.png ^
  -composite ^
  grc_s1_mrgdc.png
grc_s1_mrgdc.png

But layering in the opposite order makes the colouring more logical blah.

Here, we use the cumulative masks.

%IM%convert ^
  ( grc_s1_f1.png -fill #a00 -opaque #fff ) ^
  ( grc_s1_f2.png -transparent #000 -fill #f00 -opaque #fff ) ^
  grc_s1_msk_c_2.png ^
  -composite ^
  ( grc_s1_f3.png -transparent #000 -fill #f80 -opaque #fff ) ^
  grc_s1_msk_c_3.png ^
  -composite ^
  ( grc_s1_f4.png -transparent #000 -fill #ff0 -opaque #fff ) ^
  grc_s1_msk_c_4.png ^
  -composite ^
  grc_s1_mrgdc2.png
grc_s1_mrgdc2.png

Recording gradients

We might want to draw lines perpendicular to contours. This is the direction rainwater would take after falling in a hillside.

-statistic Gradient would give the magnitude of the gradient but not the direction. But we need both magnitude and direction.

We can calculate the x-component and y-component of the gradient separately, putting these in the red and green channels. We calculate the blue channel as the magnitude of the gradient: m=sqrt(x^2+y^2). The values are offset by 0.5, so a pixel value of 50% in a channel means zero gradient in that direction. Magnitude is always positive so the blue channel is always at least 50%.

The following code does this, using a Sobel convolution.

set AUTO=-auto-level -auto-gamma
set AUTO=

if "%IM32f%"=="" call %PICTBAT%setIm8

%IM32f%convert ^
  %GRC_SRC% ^
  -alpha off ^
  -define convolve:scale="50%%^!" -bias 50%% ^
  ( -clone 0 ^
    -morphology Convolve Sobel:0 ^
    -separate -evaluate-sequence Max ^
    %SIG% ^
    %AUTO% ^
    +write h.png ^
  ) ^
  ( -clone 0 ^
    -morphology Convolve Sobel:90 ^
    -separate -evaluate-sequence Max ^
    %SIG% ^
    %AUTO% ^
    +write v.png ^
  ) ^
  ( -clone 0 ^
    -fill Black -colorize 100 ^
  ) ^
  -delete 0 ^
  -combine ^
  -channel B ^
  -fx "hypot(u.r-0.5,u.g-0.5)+0.5" ^
  -fx "u.b==0?1:u.b" ^
  +channel ^
  +depth ^
  grc_pc0.png

call %PICTBAT%autoLevMid grc_pc0.png
grc_pc0.png grc_pc0_alm.png

The autoLevMid.bat script increases contrast, like -auto-level, but around the 50% level so that remains at 50%. We can see that the red channel increases from left to right, while the green channel increases from top to bottom.

From the image grc_pc0.png, at every pixel we know the differential of the horizontal and vertical components of the gradient. To find the perpendicular to the gradient, we simply swap the components.

Once we have a differential gradient, we can apply the inverse process (integration) to get a gradient. However, the Sobel technique above is not easily reversible. Instead, we can take simple differences horizontally and vertically.

%IM16i%convert ^
  %GRC_SRC% ^
  ( -clone 0 ^
    ( -clone 0 ^
      ( -clone 0 ) ^
      -geometry +1+0 ^
      -compose Over -composite ^
    ) ^
    -geometry +0+0 ^
    -compose Mathematics -define compose:args=0,-1,1,0.5 -composite ^
  ) ^
  ( -clone 0 ^
    ( -clone 0 ^
      ( -clone 0 ) ^
      -geometry +0+1 ^
      -compose Over -composite ^
    ) ^
    -geometry +0+0 ^
    -compose Mathematics -define compose:args=0,-1,1,0.5 -composite ^
  ) ^
  ( -clone 0 ^
    -fill gray50 -colorize 100 ) ^
  -delete 0 ^
  -combine ^
  -channel B ^
  -fx "hypot(u.r-0.5,u.g-0.5)+0.5" ^
  -fx "u.b==0?1:u.b" ^
  +channel ^
  +depth ^
  grc_pc1.png

call %PICTBAT%autoLevMid grc_pc1.png
grc_pc1.png grc_pc1_alm.png

How close is the simple difference gradient to the Sobel version? I compare the _alm versions of each.

%IM%compare -metric RMSE grc_pc0_alm.png grc_pc1_alm.png NULL: 

cmd /c exit /B 0
412.705 (0.00629747)

For this artificial image, they are very close. I expect the Sobel version would smooth out very fine detail.

For convenience, we make a script, diffGrad.bat, from the long convert command. This records the differential of the horizontal and vertical components of the gradient. It also sets the environment variable dgTOP_LEFT.

call %PICTBAT%diffGrad ^
  %GRC_SRC% grc_src1_dg.png

echo %dgTOP_LEFT% 
30.1960784313725 
grc_src1_dg.png

Inverse process

The script diffGrad.bat has created a differential gradient file. This section applies the inverse process, an integration, to make a gradient from a differential.

The red channel records the gradient horizontally, and the green channel record the gradient vertically. To reconstitute any pixel, we need the original value of one pixel, such as at coordinate (0,0). From that pixel we can walk in horizontal and vertical steps as required to reach the destination point, summing the gradients from red or green as we go.

We can apply the inverse process:

We can cumulate in the horizontal direction with -process cumulhisto, starting with zeros in the left-most column:

%IM16f%convert ^
  grc_pc1.png ^
  -separate ^
  ( -clone 0 -evaluate Subtract 50%% -process cumulhisto +write r.png ) ^
  -delete 0-2 ^
  grc_recon_h.png
grc_recon_h.png

But the left-most column shouldn't be zeros. The left-most column can be calculated as the (0,0) pixel, cumulated downwards by the green (vertical) channel. This is equivalent to cumulating from zero, then adding the (0,0) pixel.

First, we find the top-left value:

for /F "usebackq" %%C in (`%IM%convert ^
  %GRC_SRC% ^
  -precision 15 ^
  -format "TOP_LEFT=%%[fx:100*p{0,0}.r]" ^
  info:`) do set %%C

echo %TOP_LEFT% 
30.1960784313725 

Then we cumulate:

%IM16f%convert ^
  grc_pc1.png ^
  -separate ^
  ( -clone 1 -crop 1x400+0+0 +repage +write v0.png ^
    -rotate -90 ^
    -evaluate Subtract 50%% -process cumulhisto ^
    -evaluate Add %TOP_LEFT%%% ^
    -rotate 90 +write v.png ^
    -scale "600x400^!" ^
  ) ^
  -delete 0-2 ^
  grc_recon_v.png
grc_recon_v.png

We can add recon_h and recon_v:

%IM16f%convert ^
  grc_recon_h.png ^
  grc_recon_v.png ^
  -compose Add -composite ^
  grc_recon.png
grc_recon.png

How accurate is the reconstruction?

%IM%compare -metric RMSE %GRC_SRC% grc_recon.png NULL: 

cmd /c exit /B 0
25.3669 (0.000387074)

It is very accurate. So we can do the round trip from an input image to its gradient, and back (though we need to know the top-left pixel of the input).

The input is grayscale, and needs two channels to record the gradient. If the input was RGB we would need six channels.

The three convert commands can be combined and built into a script, invDiffGrad.bat.

call %PICTBAT%invDiffGrad ^
  grc_src1_dg.png %dgTOP_LEFT% grc_recon2.png
grc_recon2.png

How accurate is this reconstruction?

%IM%compare -metric RMSE %GRC_SRC% grc_recon2.png NULL: 

cmd /c exit /B 0
304.053 (0.00463955)

Not as good. Why not? Blah blah.

Perpendicular contours

If we take the inverse of the difference gradient, but swapping the horizontal and vertical vectors and negating one of them, the gradient at every pixel will be perpendicular to the original direction. If the horizontal vector is negated, the new direction gradient will be the old direction rotated by 90° clockwise. If the vertical vector is negated, it is rotated 90° anti-clockwise.

The script invDiffGrad.bat does this when idgROT is set to +1 or -1. We create both versions:

set idgROT=+1
call %PICTBAT%invDiffGrad ^
  grc_src1_dg.png %dgTOP_LEFT% grc_clk.png
grc_clk.png
set idgROT=-1
call %PICTBAT%invDiffGrad ^
  grc_src1_dg.png %dgTOP_LEFT% grc_anticlk.png

set idgROT=
grc_anticlk.png

When rotating, the invDiffGrad script auto-levels the output blah blah. If it didn't do this, pixels might be calculated to be outside the range 0 to 100%. Hence, the top-left pixel will no longer have the original value. If we are only interested in contours, this doesn't matter.

The two rotations are negatives of each other; a "hill" in one is a "dip" in the other. If we add the two images, all pixels should be virtually the same value.

%IM32f%convert ^
  grc_clk.png ^
  grc_anticlk.png ^
  -compose Add -composite ^
  -format "MINIMA=%%[fx:minima]\nMAXIMA=%%[fx:maxima]" ^
  info: 
MINIMA=0.717419
MAXIMA=0.717433

FUTURE: A process module that does an auto-level but doesn't shift a certain percentage of quantum would be useful.

From these two rotations, we can create contour images.

set FREQm1=49

%IM%convert ^
  grc_clk.png ^
  -auto-level ^
  ( -size 1x500 gradient: -rotate 90 ^
    -duplicate %FREQm1% +append ) ^
  -clut ^
  -morphology edgein diamond:1 ^
  -threshold 40%% ^
  grc_clk_sc.png
grc_clk_sc.png
set FREQm1=49

%IM%convert ^
  grc_anticlk.png ^
  -auto-level ^
  ( -size 1x500 gradient: -rotate 90 ^
    -duplicate %FREQm1% +append ) ^
  -clut ^
  -morphology edgein diamond:1 ^
  -threshold 40%% ^
  grc_anticlk_sc.png
grc_anticlk_sc.png

As expected, the contours are almost identical.

The first two rows, and some other rows, are not wonderful. This occurs when the row contains repeated values, which in turn occurs when the source image contains repeated values in columns. The process works best when the input contains values that continually vary.

We can overlay this on the original contours as a visual check that they are perpendicular.

%IM%convert ^
  grc_src1_sc.png ^
  ( grc_anticlk_sc.png ^
    -transparent Black ^
  ) ^
  -composite ^
  grc_sc_comp.png
grc_sc_comp.png

Scripts

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

mMapMasks.bat

rem Given image %1
rem and %2 image, size Nx1,
rem makes N masks, each the size of %1, white where %1 pixel is closest to that %2 pixel.
rem Also makes cumulative masks.
@rem
@rem Optional %3: base name for masks. They will be numbered 1 to N.
@rem
@rem Returns mmmNumMasks number of masks created.
@rem
@rem This is the opposite to deMapMasks.bat.
@rem
@rem Updated:
@rem   26-May-2016 for v7.

rem We could have another map file to colour the contours.
rem We could dilate the lines.


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

@setlocal

@call echoOffSave

call %PICTBAT%setInOut %1 mmm

set MAP_FILE=%2

set MASK_DD=
set MASK_PREF=
set MASK_EXT=
if not "%3"=="" (
  set MASK_DD=%~dp3
  set MASK_PREF=%~n3
  set MASK_EXT=%~x3
)

if "%MASK_PREF%"=="" set MASK_PREF=%sioCODE%
if "%MASK_EXT%"=="" set MASK_EXT=.png

set TMP_FILE=%TEMP%\%MASK_PREF%_tmp%MASK_EXT%
set SCR_FILE=%TEMP%\%MASK_PREF%_scr.bat
set CUMUL_SCR_FILE=%TEMP%\%MASK_PREF%_cumul_scr.bat

set HH=
set NumMasks=
for /F "usebackq" %%L in (`%IM%identify ^
  -format "HH=%%h\nNumMasks=%%w" ^
  %MAP_FILE%`) do set %%L

if not "%HH%"=="1" (
  echo %0: map file [%MAP_FILE%] not height 1
  exit /B 1
)
if "%NumMasks%"=="" exit /B 1

%IM%convert %INFILE% +dither -remap %MAP_FILE% %TMP_FILE%
if ERRORLEVEL 1 (
  echo %0: [%1] [%2] convert failed
  exit /B 1
)

echo %IM%convert %TMP_FILE% -fill Black -background White >%SCR_FILE%
echo %IM%convert -compose Lighten >%CUMUL_SCR_FILE%

set CNT=1
for /F "usebackq skip=1 tokens=6 delims=,:() " %%C in (`%IM%convert ^
  %MAP_FILE% ^
  -colorspace sRGB ^
  txt:`) do (
  echo %%C

  set MASK_FILE=%MASK_DD%%MASK_PREF%!CNT!%MASK_EXT%
  set CUMUL_FILE=%MASK_DD%%MASK_PREF%c_!CNT!%MASK_EXT%

  echo ^( +clone -transparent %%C -colorize 100 -flatten +write !MASK_FILE! +delete ^) >>%SCR_FILE%

  echo !MASK_FILE! >>%CUMUL_SCR_FILE%

  if !CNT! GTR 1 (
    echo -composite +write !CUMUL_FILE! >>%CUMUL_SCR_FILE%
  )

  set /A CNT+=1
)

cPrefix /i%SCR_FILE% /r^^ 
echo NULL: >>%SCR_FILE%
cPrefix /i%CUMUL_SCR_FILE% /r^^ 
echo NULL: >>%CUMUL_SCR_FILE%

rem %IM%convert "@%SCR_FILE%" NULL:

type %SCR_FILE%
call %SCR_FILE%

type %CUMUL_SCR_FILE%
call %CUMUL_SCR_FILE%

call echoRestore

endlocal & set mmmOUTFILE=%OUTFILE%& set mmmNumMasks=%NumMasks%

autoLevMid.bat

rem Makes auotolevel version, such that 50% remains at 50%.
rem Output %2 will have 0% or 100%, but generally not both.
rem %3 if not 0, will first add 50% tp the image.
@rem
@rem Updated:
@rem   25-June-2017 Added "ADDHALF" facility.
@rem

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

@setlocal

@call echoOffSave

call %PICTBAT%setInOut %1 alm

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

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

if "%ADDHALF%"=="0" (
  set sADDHALF=
) else (
  set sADDHALF=-evaluate Add 50%%
)

if "%IM32f%"=="" call %PICTBAT%setIm8

for /F "usebackq" %%L in (`%IM32f%convert ^
  %INFILE% ^
  %sADDHALF% ^
  -format "dm=%%[fx:abs(minima-0.5)>abs(maxima)?minima:maxima]" ^
  info:`) do set %%L

echo dm=%dm%

for /F "usebackq" %%L in (`%IM%identify ^
  -format "dm=%%[fx:100*(%dm%>0.5?1-%dm%:%dm%)]" ^
  xc:`) do set %%L

echo dm=%dm%

for /F "usebackq" %%L in (`%IM%identify ^
  -format "dmo=%%[fx:100-%dm%]" ^
  xc:`) do set %%L

echo dmo=%dmo%

%IM32f%convert ^
  %INFILE% ^
  %sADDHALF% ^
  -level %dm%,%dmo%%% ^
  %OUTFILE%


call echoRestore

endlocal & set almOUTFILE=%OUTFILE%&

diffGrad.bat

@rem See also invDiffGrad.bat.


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

@setlocal

@call echoOffSave

call %PICTBAT%setInOut %1 dg

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


for /F "usebackq" %%L in (`%IM16i%convert ^
  %INFILE% ^
  -precision 15 ^
  -format "TOP_LEFT=%%[fx:100*p{0,0}.r]" ^
  +write info: ^
  ^( -clone 0 ^
    ^( -clone 0 ^
      ^( -clone 0 ^) ^
      -geometry +1+0 ^
      -compose Over -composite ^
    ^) ^
    -geometry +0+0 ^
    -compose Mathematics -define "compose:args=0,-1,1,0.5" -composite ^
  ^) ^
  ^( -clone 0 ^
    ^( -clone 0 ^
      ^( -clone 0 ^) ^
      -geometry +0+1 ^
      -compose Over -composite ^
    ^) ^
    -geometry +0+0 ^
    -compose Mathematics -define "compose:args=0,-1,1,0.5" -composite ^
  ^) ^
  ^( -clone 0 ^
     -fill gray50 -colorize 100 ^) ^
  -delete 0 ^
  -combine ^
  -channel B ^
  -fx "hypot^(u.r-0.5,u.g-0.5^)+0.5" ^
  -fx "u.b==0?1:u.b" ^
  +channel ^
  +depth ^
  %OUTFILE%`) do set %%L


call echoRestore

endlocal & set dgOUTFILE=%OUTFILE%& set dgTOP_LEFT=%TOP_LEFT%

invDiffGrad.bat

@rem See also diffGrad.bat.


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

@setlocal

rem @call echoOffSave

call %PICTBAT%setInOut %1 idg

if "%IM32f%"=="" call %PICTBAT%setIm8
if "%IM32f%"=="" (
  echo %0: IM32f not available.
  exit /B 1
)

set TOP_LEFT=%2
if "%TOP_LEFT%"=="." set TOP_LEFT=
if "%TOP_LEFT%"=="" set TOP_LEFT=50

if not "%3"=="" set OUTFILE=%3

if "%idgROT%"=="" set idgROT=0

set TMPFILE=%~n1_idg_tmp.miff

rem The rotation is wrong.
rem The red channel has zero in the first column,
rem so can't be cumulated for vertical.

if %idgROT% EQU 0 (
  set CLONE_H=0
  set CLONE_V=1
  set NEG_H=
  set NEG_V=
) else if %idgROT% EQU -1 (
  set CLONE_H=1
  set CLONE_V=0
  set NEG_H=
  set NEG_V=-negate
) else if %idgROT% EQU 1 (
  set CLONE_H=1
  set CLONE_V=0
  set NEG_H=-negate
  set NEG_V=
)

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

rem in -crop, +1 is a kludge.

%IM32f%convert ^
  "%INFILE%" ^
  -separate ^
  ( -clone %CLONE_H% ^
    %NEG_H% ^
    -evaluate Subtract 50%% ^
    -process cumulhisto ^
  ) ^
  ( -clone %CLONE_V% -crop 1x%HH%+1+0 +repage ^
    %NEG_V% ^
    -rotate -90 ^
    -evaluate Subtract 50%% ^
    -process cumulhisto ^
    -evaluate Add %TOP_LEFT%%% ^
    -rotate 90 ^
    -scale "%WW%x%HH%^!" ^
  ) ^
  -delete 0-2 ^
  -evaluate-sequence Add ^
-format "%%[fx:minima] %%[fx:maxima]" +write info: ^
  -define quantum:format=floating-point -depth 32 ^
  "%TMPFILE%"

call %PICTBAT%shiftNoNeg %TMPFILE% %OUTFILE%


call echoRestore

endlocal & set idgOUTFILE=%OUTFILE%&

shiftNoNeg.bat

rem Given image %1 that may contain negative values,
rem writes output with no negative values.

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

@setlocal

rem @call echoOffSave

call %PICTBAT%setInOut %1 snn

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

set MIN_PC=
for /F "usebackq" %%L in (`%IM32f%convert ^
  "%INFILE%" ^
  -format "MIN=%%[fx:minima]\nMIN_PC=%%[fx:(-minima)*100]\nMIN_LSS_Z=%%[fx:minima<0?1:0]\n" ^
  info:`) do set %%L

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

echo MIN_PC=%MIN_PC%  MIN_LSS_Z=%MIN_LSS_Z%

if "%MIN_LSS_Z%"=="1" (
  set sADD=-evaluate Add %MIN_PC%%% -clamp
) else (
  set sADD=
)

%IM32f%convert ^
  "%INFILE%" ^
  %sADD% ^
  "%OUTFILE%"


call echoRestore

endlocal & set snnOUTFILE=%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 gradcont.h1. To re-create this web page, run "procH1 gradcont".


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 27-June-2015.

Page created 27-Jun-2017 17:00:42.

Copyright © 2017 Alan Gibson.