snibgo's ImageMagick pages

Putting OOG back in the box

When pixels have become out of gamut, how do we put them back in?

Some operations result in colours that are out of gamut ("OOG"), that is, some pixels have values outside the range of 0 to 100% of QuantumRange. If we are using integer IM, or saving to an integer file format, then these values will be clamped (or "clipped") to 0 or 100%, and any detail in those pixels is lost, and cannot be reconstructed. We won't even know which pixels were OOG, though we might guess that any values at 0 or 100% were OOG, and we might attempt reconstruction from nearby pixels that are not at the limits.

Being out of gamut is not, of itself, a problem. We can continue processing with floating-point. But, eventually, we usually want to bring all the colours within gamut, if only because output devices can't display OOG colors.

This page isn't about recovering data that has been lost due to clipping. It is about preventing the loss in the first place.

Sample input

For example, from the Colours to matrix and polynomials page, where the intention is to change toes.png to look more like toes_x_holed.png:

set SRC=ob_src.miff

set FMTrgb=^
red: %%[fx:minima.r] to %%[fx:maxima.r]\n^
green: %%[fx:minima.g] to %%[fx:maxima.g]\n^
blue: %%[fx:minima.b] to %%[fx:maxima.b]\n

%IMDEV%convert ^
  toes.png ^
  toes_x_holed.png ^
  -process 'cols2mat weightAlpha' ^
  -depth 32 ^
  -define quantum:format=floating-point ^
  -format "%FMTrgb%" ^
  +write info: ^
  %SRC% 
red: -0.460739 to 1.74338
green: -0.141132 to 1.23285
blue: -0.426488 to 1.23798
ob_src.miffjpg

We have shown the range of values in each channel as multiples of QuantumRange, and saved the image to a floating-point miff (which you can download: ob_src.miff). For display on the web, the image has been clamped and converted to JPEG. Notice how the skin-tone at the right-edge bleeds into the white page background.

Working in floating-point, we can readily isolate pixels where a channel is more than 100%, and where a channel is less than zero.

%IMDEV%convert ^
  %SRC% ^
  -channel RGB ^
  -threshold 100%% ^
  +channel ^
  +write ob_th3hi.png ^
  -fill White +opaque Black ^
  -format %%[fx:mean] ^
  info: 
0.156355

This proportion of pixels are >100%.

ob_th3hi.png
%IMDEV%convert ^
  %SRC% ^
  -channel RGB ^
  -negate ^
  -threshold 100%% ^
  +channel ^
  +write ob_th3lo.png ^
  -fill White +opaque Black ^
  -format %%[fx:mean] ^
  info: 
0.0751957

This proportion of pixels are <0%.

ob_th3lo.png

In the first result, most pixels are black. In those positions, pixels have not exceeded 100%. The red pixels are where only the red channel exceeded 100%. Yellow pixels are where both red and green exceeded 100%. The small number of white pixels are where all three channels exceeded 100%.

The second result works in a similar way: it is blue where the blue channel was less than zero, magenta where both red and blue were less zero, and so on.

How can we process the image to bring all pixels into gamut?

Clamp

We can explicitly clamp (or "clip") the pixel values:

%IMDEV%convert ^
  %SRC% ^
  -clamp ^
  -depth 32 ^
  -define quantum:format=floating-point ^
  -format "%FMTrgb%" ^
  +write info: ^
  ob_clmp.miff 
red: 0 to 1
green: 0 to 1
blue: 0 to 1
ob_clmp.miffjpg

This has clamped values beyond 0 or 100% to those limits. Detail is lost, especially in parts of the sandal top-right of the image. (Many pixels that were different have become the same, pure white.)

Auto-level

Instead of clamping, we can "-auto-level". This applies a gain and bias that modifies all pixels, probably reducing contrast, squishing values together so they fit in the range. The same transformation is usually applied to all the channels, though they can be processed separately (but this will create a colour cast).

%IMDEV%convert ^
  %SRC% ^
  -auto-level ^
  ob_al.jpg
ob_al.jpg
%IMDEV%convert ^
  %SRC% ^
  -channel RGB ^
  -auto-level ^
  +channel ^
  ob_al2.jpg
ob_al2.jpg

This has retained all the detail in all the pixels. Applying the same transformation to all channels has reduced contrast heavily. Processing the channels separately hasn't reduced contrast as much, but has created a colour cast.

For some purposes, the mean of clamped and auto-levelled versions is satisfactory, though it does lower the contrast even in the mid-tones:

%IMDEV%convert ^
  %SRC% ^
  ( -clone 0 -clamp )^
  ( -clone 0 -auto-level )^
  -delete 0 ^
  -evaluate-sequence Mean ^
  ob_clmpal.png
ob_clmpal.pngjpg

Linear modulation

Another option is to modulate all values outside certain limits, say all values above 0.9 or below 0.1. The modulation is by a linear function (the usual gain-and-bias) where a value at 0.9 is unchanged but greater values are progressively reduced so the highest value is transformed to 1.0. We modulate values below 0.1 in a similar way. The overall transformation is shown in green on this diagram:

ob_diag.png

In the diagram, in-gamut colours are in the square between (0,0) and (1,1). Input values are between x0 and x1, where x0<0 and x1>1. We want the output values to be between 0 and 1. Most input values, those between P0 and P1 (where P0<P1), are unchanged. Input values between x0 and P0 will be transformed linearly by y=a*x+b, and values between P1 and x1 will be transformed by y=c*x+d, where a, b, c and d are calculated from x0, x1, P0 and P1:

a = -P0 / (x0 - P0)
b = P0 * x0 / (x0 - P0)
c = (1-P1) / (x1-P1)
d = P1 * (x1-1) / (x1-P1)

Highlights and shadows are processed independently. OOG may occur in highlights, or shadows, or both.

This will reduce contrast where the input is less than P0 or more than P1, and will cause hue-shift for RGB pixels that have channels straddling P0 or P1. Setting P0 to 0.0 will clip shadows; setting P1 to 1.0 will clip highlights.

Three methods are shown. The first operates on all colour channels (usually red, green and blue), applying the same transformation to each channel. The second operates on all colour channels, applying independent transformations to each channel. The third operates on the L channel of HCLp.

All colour channels, same transformation

call %PICTBAT%oogLinear ^
  "%SRC%" ob_linsam.png 
ob_linsam.pngjpg
P0=0.1 P1=0.9
DO_LO=1 DO_HI=1
X0=-0.4607392476081706 X1=1.743379980731611
a=0.1783360098772278 b=0.08216639901227722 c=0.1185705165935436 d=0.7932865350658107

All colour channels, independently

The script oogLinear3.bat separates the image, makes three independent calls to oogLinear.bat, and combines the results.

call %PICTBAT%oogLinear3 ^
  "%SRC%" ob_linind.png 
ob_linind.pngjpg
P0=0.1 P1=0.9
DO_LO=1 DO_HI=1
X0=-0.4607392476081706 X1=1.743379980731611
a=0.1783360098772278 b=0.08216639901227722 c=0.1185705165935436 d=0.7932865350658107
P0=0.1 P1=0.9
DO_LO=1 DO_HI=1
X0=-0.1411316916675148 X1=1.23284757166003
a=0.4147111452188762 b=0.05852888547811239 c=0.3004378235396586 d=0.6296059588143071
P0=0.1 P1=0.9
DO_LO=1 DO_HI=1
X0=-0.426487531612275 X1=1.237979159047357
a=0.1899380213122383 b=0.08100619786877618 c=0.2958762317826472 d=0.6337113913956175

Lightness only

call %PICTBAT%oogLinear ^
  "%SRC% -colorspace HCLp -channel B -separate" 
P0=0.1 P1=0.9
DO_LO=1 DO_HI=1
X0=-0.1966666896354097 X1=1.347225625381625
a=0.3370786255878461 b=0.0662921374412154 c=0.2236007829709407 d=0.6987592953261534
set FMThclp=^
H: %%[fx:minima.r] to %%[fx:maxima.r]\n^
C: %%[fx:minima.g] to %%[fx:maxima.g]\n^
L: %%[fx:minima.b] to %%[fx:maxima.b]\n

%IMDEV%convert ^
  %SRC% ^
  -colorspace HCLp ^
  -depth 32 ^
  -define quantum:format=floating-point ^
  -format "%FMThclp%" ^
  info: 
H: 3.51761e-05 to 0.999987
C: 0.0115371 to 0.843692
L: -0.196667 to 1.34723

[No image]

In HCLp colorspace, only the L (Luma) channel of this image has OOG values. (In general, the C channel may also be OOG.)

%IMDEV%convert ^
  %SRC% ^
  -colorspace HCLp ^
  -separate ^
  -depth 32 ^
  -define quantum:format=floating-point ^
  ob_hclp.miff

call %PICTBAT%oogLinear ^
  ob_hclp.miff[2] ob_hclp_lcorr.miff 

[No image]

P0=0.1 P1=0.9
DO_LO=1 DO_HI=1
X0=-0.1966666896354097 X1=1.347225625381625
a=0.3370786255878461 b=0.0662921374412154 c=0.2236007829709407 d=0.6987592953261534
%IMDEV%convert ^
  ob_hclp.miff ^
  ob_hclp_lcorr.miff ^
  -swap 2,-1 ^
  +delete ^
  -combine ^
  -set colorspace HCLp ^
  -depth 32 ^
  -define quantum:format=floating-point ^
  -format "%FMThclp%" ^
  +write info: ^
  -colorspace sRGB ^
  -format "%FMTrgb%" ^
  +write info: ^
  ob_hclp_lin.miff 
H: 3.51761e-05 to 0.999987
C: 0.0115371 to 0.843692
L: 5.65073e-09 to 1
red: 0 to 1
green: 8.72654e-09 to 1
blue: 0 to 1
ob_hclp_lin.miffjpg

The script oogLinear3.bat is more convenient:

%IMDEV%convert ^
  %SRC% ^
  -colorspace HCLp ^
  -depth 32 ^
  -define "quantum:format=floating-point" ^
  ob_src_hclp.miff

call %PICTBAT%oogLinear3 ^
  ob_src_hclp.miff ob_hclps2.miff 

%IMDEV%convert ^
  ob_hclps2.miff ^
  -colorspace sRGB ^
  -depth 32 ^
  -define "quantum:format=floating-point" ^
  ob_hclp2_lin.miff
ob_hclp2_lin.miffjpg
P0=0.1 P1=0.9
DO_LO=0 DO_HI=0
X0=3.517605365141669e-05 X1=0.9999866599682687
a=1.00035188431553 b=-3.518843155297885e-005 c=1.000133418115331 d=-0.0001200763037983422
P0=0.1 P1=0.9
DO_LO=0 DO_HI=0
X0=0.01153713092476528 X1=0.843692105459909
a=1.130417779180928 b=-0.01304177791809277 c=-1.77594990572415 d=2.498354915151735
P0=0.1 P1=0.9
DO_LO=1 DO_HI=1
X0=-0.1966666896354097 X1=1.347225625381625
a=0.3370786255878461 b=0.0662921374412154 c=0.2236007829709407 d=0.6987592953261534

Power modulation

Instead of a linear transformation at each end, we can use power curves (sometimes called filmic curves, as they mimic the response of photographic film). These will have slopes of zero at (x0,0) and (x1,1), and one at P0 and P1.

The power curve with zero gradient at the origin (0,0) is:

y = A*x^B

The gradient is:

y' = B*A*x^(B-1)

Taking logs of the curve:

ln(y) = ln(A*x^B)
      = lnA + B*ln(x)

lnA = ln(y) - B*ln(x)

Taking logs of the gradient:

ln(y') = ln(B*A*x^(B-1))
       = ln(B) + lnA + (B-1)*ln(x)

Substitute lnA:

ln(y') = ln(B) + ln(y) - B*ln(x) + (B-1)*ln(x)
       = ln(B) + ln(y) - ln(x)
y'= B*y/x
B = y'*x/y

So we can calculate B and lnA from a known gradient, at P0 or P1.

At the toe, the curve origin is at (x0,0), and the slope at P0 is one, so:

B0 = (P0-x0)/P0
lnA0 = ln(P0) - B0*ln(P0-x0)
A0 = P0/(P0-x0)^B0

... and we will use the equation y = A0*(x-x0)^B0. The gradient of the toe curve is y' = B0*A0*(x-x0)^(B0-1).

At the shoulder, the origin is at (x1,1), and the slope at P1 is one, and both axes are negated, so:

B1 = (x1-P1)/(1-P1)
A1 = (1-P1)/(x1-P1)^B1

... and we will use the equation y = 1 - A1*(x1-x)^B1. The gradient of the shoulder curve is y' = B1*A1*(x1-x)^(B1-1).

As for the linear transformation, this method can be applied to all channels together or independently, or just to a lightness channel. Here, we use only one method: all channels together. The script oogPower.bat implements this, using "-fx".

call %PICTBAT%oogPower ^
  "%SRC%" ob_powsam.png 
ob_powsam.pngjpg
P0=0.1 P1=0.9
DO_LO=1 DO_HI=1
X0=-0.4607392476081706 X1=1.743379980731611
A0=2.563279315872661 B0=5.607392476081706 A1=0.4206343929130026 B1=8.433799807316111
min=7.07458e-34  max=1
See also Filmic Tonemapping with Piecewise Power Curves, John Hable, 2017.

Comparison

Most of the results have differences that are subtle. For comparison purposes, we make a GIF of the results that are similar.

%IM%convert ^
  -loop 0 ^
  -delay 100 ^
  -gravity NorthWest -fill White ^
  ( ob_clmp.miff      -annotate +10+10 "Clamp" ) ^
  ( ob_clmpal.png     -annotate +10+10 "ClampAL" ) ^
  ( ob_linsam.png     -annotate +10+10 "LinSame" ) ^
  ( ob_linind.png     -annotate +10+10 "LinInd" ) ^
  ( ob_hclp_lin.miff  -annotate +10+10 "HCLP" ) ^
  ( ob_hclp2_lin.miff -annotate +10+10 "HCLP2" ) ^
  ( ob_powsam.png     -annotate +10+10 "PowSame" ) ^
  ob_all.gif
ob_all.gif

Which method is best?

I currently have no conclusion about the "best" method.

Process module

A process module would be faster, especially for the independent version, and could be incorporated into "convert" or "magick" commands. Options would include:

Retinex

Future

When an image is encoded HCLp, and we convert to sRGB, is the sRGB image guaranteed to be in gamut? No. Hence, if we use HCLp to try and bring an sRGB OOG back into gamut, we should then test the sRGB result and apply a suitable process to that.

Can we invert the process? We haven't lost any pixel data, but we have lost the knowledge of how far OOG the image was. We can't reverse the process without this knowledge. If we know the original x0, x1, P0 and P1, and hence a, b, c and d, we can calculate the inverse transformation:

a' = 1/a
b' = x0
c' = 1/c
d' = x1 - c'

Instead of linear transformations y=a*x+b etc, we could use spline curves, (eg from P1 in the direction of (1,1), to x1 from the direction of (1,1)), or cubic curves.

Currently P0 and P1 are parameters from the user, defaulting to 0.0 and 0.9. Perhaps sensible values could be calculated somehow.

Scripts

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

oogLinear.bat

@rem From %1, a floating-point image that may contain values out of 0 to 100%,
@rem makes output %2 with all values within 0 to 100%.
@rem %3 limit for lower transformation.
@rem %4 limit for upper transformation.


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

@setlocal enabledelayedexpansion

@call echoOffSave

rem call %PICTBAT%setInOut %1 olin


set INFILE=%~1

set OUTFILE=%2
if "%OUTFILE%"=="." set OUTFILE=
if "%OUTFILE%"=="" set OUTFILE=NULL:

set P0=%3
if "%P0%"=="." set P0=
if "%P0%"=="" set P0=0.1

set P1=%4
if "%P1%"=="." set P1=
if "%P1%"=="" set P1=0.9

set X0=
for /F "usebackq" %%L in (`%IMDEV%convert ^
  %INFILE% ^
  -precision 16 ^
  -format "X0=%%[fx:minima]\nX1=%%[fx:maxima]\n" ^
  info:`) do set %%L
if "%X0%"=="" exit /B 1

:: FIXME: if X0 > -epsilon, don't calc a and b.
:: FIXME: if X1 < 1+epsilon, don't calc c and d.

set DO_LO=0
set DO_HI=0

set EPS=1e-5

set FMT=^
DO_LO=%%[fx:%X0%^<-%EPS%?1:0]\n^
DO_HI=%%[fx:%X1%^>1+%EPS%?1:0]\n

for /F "usebackq" %%L in (`%IM%identify ^
  -format "%FMT%" ^
  xc:`) do set %%L

set FMT=^
a=%%[fx:(-%P0%)/(%X0%-(%P0%))]\n^
b=%%[fx:%P0%*(%X0%)/(%X0%-(%P0%))]\n

for /F "usebackq" %%L in (`%IM%identify ^
  -precision 16 ^
  -format "%FMT%" ^
  xc:`) do set %%L

set FMT=^
c=%%[fx:(1-%P1%)/(%X1%-%P1%)]\n^
d=%%[fx:%P1%*(%X1%-1)/(%X1%-%P1%)]\n

for /F "usebackq" %%L in (`%IM%identify ^
  -precision 16 ^
  -format "%FMT%" ^
  xc:`) do set %%L


echo P0=%P0% P1=%P1%
echo DO_LO=%DO_LO% DO_HI=%DO_HI%
echo X0=%X0% X1=%X1%
echo a=%a% b=%b% c=%c% d=%d%

goto skip

%IMDEV%convert ^
  %INFILE% ^
  ( -clone 0 ^
    -function Polynomial %c%,%d% ^
  ) ^
  ( -clone 0 ^
    -function Polynomial %a%,%b% ^
  ) ^
  ( -clone 0,1 ^
    -compose Darken -composite ^
  ) ^
  -delete 0,1 ^
  -compose Lighten -composite ^
  -depth 32 ^
  -define quantum:format=floating-point ^
  %OUTFILE%

:skip

set POLY_HI=
set POLY_LO=

if %DO_HI%==1 set POLY_HI=^
  ( -clone 0 ^
    -function Polynomial %c%,%d% ^
  ) ^
  -compose Darken -composite

if %DO_LO%==1 set POLY_LO=^
  ( -clone 0 ^
    -function Polynomial %a%,%b% ^
  ) ^
  -compose Lighten -composite

%IMDEV%convert ^
  %INFILE% ^
  -define compose:clamp=off ^
  %POLY_HI% ^
  %POLY_LO% ^
  -depth 32 ^
  -define quantum:format=floating-point ^
  %OUTFILE%


call echoRestore

@endlocal & set olinOUTFILE=%OUTFILE%

oogLinear3.bat

@rem Applies oogLinear independently to three channels.

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

@setlocal enabledelayedexpansion

@call echoOffSave

rem call %PICTBAT%setInOut %1 olin


set INFILE=%~1

set OUTFILE=%2
if "%OUTFILE%"=="." set OUTFILE=
if "%OUTFILE%"=="" set OUTFILE=NULL:

set P0=%3
if "%P0%"=="." set P0=
if "%P0%"=="" set P0=0.1

set P1=%4
if "%P1%"=="." set P1=
if "%P1%"=="" set P1=0.9

set TMP_FILE=\temp\ooglin3_%%d.miff
set TMP0=\temp\ooglin3_0.miff
set TMP1=\temp\ooglin3_1.miff
set TMP2=\temp\ooglin3_2.miff


for /F "usebackq" %%L in (`%IMDEV%convert ^
  %INFILE% ^
  -format "COLSP=%%[colorspace]\n" ^
  +write info: ^
  -channel RGB ^
  -separate ^
  +channel ^
  -depth 32 ^
  -define "quantum:format=floating-point" ^
  +adjoin ^
  %TMP_FILE%`) do set %%L

call %PICTBAT%oogLinear %TMP0% %TMP0% %P0% %P1%
if ERRORLEVEL 1 exit /B 1
call %PICTBAT%oogLinear %TMP1% %TMP1% %P0% %P1%
if ERRORLEVEL 1 exit /B 1
call %PICTBAT%oogLinear %TMP2% %TMP2% %P0% %P1%
if ERRORLEVEL 1 exit /B 1

%IMDEV%convert ^
  %TMP0% %TMP1% %TMP2% ^
  -combine ^
  -set colorspace %COLSP% ^
  -depth 32 ^
  -define "quantum:format=floating-point" ^
  %OUTFILE%

call echoRestore

@endlocal & set olinOUTFILE=%OUTFILE%

oogLinear1ch.bat

@rem From %1, a floating-point image that may contain values out of 0 to 100%,
@rem makes output %2 with all values within 0 to 100%.
@rem %3 limit for lower transformation.
@rem %4 limit for upper transformation.
@rem %5 colorspace that contains a lightness channel (eg Lab, HCL).
@rem %6 number of lightness channel (0, 1 or 2).

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

@setlocal enabledelayedexpansion

@call echoOffSave

rem call %PICTBAT%setInOut %1 olin


set INFILE=%~1

set OUTFILE=%2
if "%OUTFILE%"=="." set OUTFILE=
if "%OUTFILE%"=="" set OUTFILE=NULL:

set P0=%3
if "%P0%"=="." set P0=
if "%P0%"=="" set P0=0.1

set P1=%4
if "%P1%"=="." set P1=
if "%P1%"=="" set P1=0.9

set COLSP=%5
if "%COLSP%"=="." set COLSP=
if "%COLSP%"=="" set COLSP=Lab

set CHNUM=%6
if "%CHNUM%"=="." set CHNUM=
if "%CHNUM%"=="" (
  if /I %COLSP%==Lab set CHNUM=0
)
if "%CHNUM%"=="" exit / 1

echo COLSP=%COLSP%  CHNUM=%CHNUM%

set TMP_PREF=\temp\ooglin1ch

set TMP_FILE=%TMP_PREF%_%%d.miff
set TMP0=%TMP_PREF%_0.miff
set TMP1=%TMP_PREF%_1.miff
set TMP2=%TMP_PREF%_2.miff

set TMP_CH=%TMP_PREF%_%CHNUM%.miff

for /F "usebackq" %%L in (`%IMDEV%convert ^
  %INFILE% ^
  -format "OrigCOLSP=%%[colorspace]\n" ^
  +write info: ^
  -colorspace %COLSP% ^
  -channel RGB ^
  -separate ^
  +channel ^
  -depth 32 ^
  -define "quantum:format=floating-point" ^
  +adjoin ^
  %TMP_FILE%`) do set %%L

echo OrigCOLSP=%OrigCOLSP%

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

call %PICTBAT%oogLinear %TMP_CH% %TMP_CH% %P0% %P1%
if ERRORLEVEL 1 exit /B 1

%IMDEV%convert ^
  %TMP0% %TMP1% %TMP2% ^
  -combine ^
  -set colorspace %COLSP% ^
  -colorspace %OrigCOLSP% ^
  -depth 32 ^
  -define "quantum:format=floating-point" ^
  %OUTFILE%


call echoRestore

@endlocal & set olinOUTFILE=%OUTFILE%

oogPower.bat

@rem From %1, a floating-point image that may contain values out of 0 to 100%,
@rem makes output %2 with all values within 0 to 100%.
@rem %3 limit for lower transformation.
@rem %4 limit for upper transformation.


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

@setlocal enabledelayedexpansion

@call echoOffSave

rem call %PICTBAT%setInOut %1 opow


set INFILE=%~1

set OUTFILE=%2
if "%OUTFILE%"=="." set OUTFILE=
if "%OUTFILE%"=="" set OUTFILE=NULL:

set P0=%3
if "%P0%"=="." set P0=
if "%P0%"=="" set P0=0.1

set P1=%4
if "%P1%"=="." set P1=
if "%P1%"=="" set P1=0.9

set X0=
for /F "usebackq" %%L in (`%IMDEV%convert ^
  %INFILE% ^
  -precision 16 ^
  -format "X0=%%[fx:minima]\nX1=%%[fx:maxima]\n" ^
  info:`) do set %%L
if "%X0%"=="" exit /B 1

set DO_LO=0
set DO_HI=0

set EPS=1e-5

set FMT=^
DO_LO=%%[fx:%X0%^<-%EPS%?1:0]\n^
DO_HI=%%[fx:%X1%^>1+%EPS%?1:0]\n

for /F "usebackq" %%L in (`%IM%identify ^
  -format "%FMT%" ^
  xc:`) do set %%L

set FMT=^
B0=%%[fx:(%P0%-(%X0%))/(%P0%)]\n^
B1=%%[fx:(%x1%-%P1%)/(1-%P1%)]\n

for /F "usebackq" %%L in (`%IM%identify ^
  -precision 16 ^
  -format "%FMT%" ^
  xc:`) do set %%L

set FMT=^
A0=%%[fx:(%P0%)/pow(%P0%-(%X0%),%B0%)]\n^
A1=%%[fx:(1-%P1%)/pow(%X1%-%P1%,%B1%)]\n

for /F "usebackq" %%L in (`%IM%identify ^
  -precision 16 ^
  -format "%FMT%" ^
  xc:`) do set %%L


echo P0=%P0% P1=%P1%
echo DO_LO=%DO_LO% DO_HI=%DO_HI%
echo X0=%X0% X1=%X1%
echo A0=%A0% B0=%B0% A1=%A1% B1=%B1%

:: Only do each end if OOG.

set DO_BOTH=0
if %DO_LO%==1 if %DO_HI%==1 set DO_BOTH=1

if %DO_BOTH%==1 (
  set "sFX=u<%P0%?%A0%*pow(u-(%X0%),%B0%):u>%P1%?1-%A1%*pow(%X1%-u,%B1%):u"
) else if %DO_LO%==1 (
  set "sFX=u<%P0%?%A0%*pow(u-(%X0%),%B0%):u"
) else if %DO_HI%==1 (
  set "sFX=u>%P1%?1-%A1%*pow(%X1%-u,%B1%):u"
) else (
  set sFX=u
)

%IMDEV%convert ^
  %INFILE% ^
  -fx "%sFX%" ^
  -depth 32 ^
  -define quantum:format=floating-point ^
  -format "min=%%[fx:minima]  max=%%[fx:maxima]\n" ^
  +write info: ^
  %OUTFILE%

call echoRestore

@endlocal & set opowOUTFILE=%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 oogbox.h1. To re-create this web page, execute "procH1 oogbox".


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 15-November-2017.

Page created 02-Dec-2017 11:17:14.

Copyright © 2017 Alan Gibson.