﻿

What level?

Assuming that one image has been transformed to another by "-level", what were the parameters?

The problem might occur because we have used "-contrast-stretch", and want to know the equivalent "-level".

ASIDE. This page is one of a series that address the question: "What colour transformation makes this image look like that image?"

The pages in that series, roughly from simple to complex, are:

Script whatLevel.bat

We assume one image has been transformed to another by a "-level" operation, or possibly three operations (one for each RGB channel), or "contrast-stretch". We have the input and result images. The task is to find the two percentage parameters of each "-level".

The "-level" operation is linear, so a pair of input pixels, and the corresponding result pixels, are sufficient to define the parameters provided the pair of pixels are different in all channels, and clipping has not occurred.

By default, the script will use pixels at the top-left and bottom-right corners. The user can choose different coordinates. If the input pixels values are integer, for good precision we should choose a pair that is distant within the colour-cube; basically, choose a light pixel and a dark pixel, but not black or white in the result image.

[What if +level was used?]

[Linear regression]

[cf Gain and Bias]

Suppose we had "-level L1,L2" where L1 and L2 are proportions of QuantumRange, [0..1]. The linear transformation is:

`vout = (vin-L1) / (L2-L1)`

... where vin and vout are [0..1].

In each pair of pixels, we number them 0 and 1. So we have two simultaneous equations:

```vout0 = (vin0-L1) / (L2-L1) = vin0/(L2-L1) - L1/(L2-L1)
vout1 = (vin1-L1) / (L2-L1) = vin1/(L2-L1) - L1/(L2-L1)```

By subtraction:

```vout0-vout1 = vin0/(L2-L1) - vin1/(L2-L1)
= (vin0-vin1) / (L2-L1)```

So:

```L2-L1 = (vin0-vin1) / (vout0-vout1)

L1 = vin0 - vout0 * (L2-L1)

L2 = (L2-L1) + L1```

Thus, we can calculate L1 and L2 for each channel.

The script whatLevel.bat does the work.

 toes.png Increase contrast slightly: ```%IMG7%magick ^ toes.png ^ -level 5,95%% ^ wl_samp1.png```

Calculate the parameters:

`call %PICTBAT%whatLevel toes.png wl_samp1.png wl_samp1`

List the parameters:

`set wl_samp1 `
```wl_samp1_L1_B=5.00077314512755
wl_samp1_L1_G=5.00007629511308
wl_samp1_L1_R=5.01276829756772
wl_samp1_L2_B=94.9977015776046
wl_samp1_L2_G=95.0000762951048
wl_samp1_L2_R=94.9790008108331
wl_samp1_strLevel=-channel R -level 5.01276829756772x94.9790008108331% -channel G -level 5.00007629511308x95.0000762951048% -channel B -level 5.00077314512755x94.9977015776046% +channel```

As we might hope, the parameters are more or less the same for the three channels, and are more or less correct.

For convenience, the script also creates an environment variable that can apply the "-level" commands, like this:

 ```%IMG7%magick ^ toes.png ^ %wl_samp1_strLevel% ^ wl_appd.png```

If we want only one "-level", we can set wlPreProc to convert to grayscale, then back to sRGB so we have three channels:

```set wlPreProc=-colorspace Gray -colorspace sRGB
call %PICTBAT%whatLevel toes.png wl_samp1.png wl_samp1a
set wlPreProc=```

List the parameters:

`set wl_samp1a `
```wl_samp1a_L1_B=5.01453560865443
wl_samp1a_L1_G=5.01453560865443
wl_samp1a_L1_R=5.01453560865443
wl_samp1a_L2_B=94.9791519744924
wl_samp1a_L2_G=94.9791519744924
wl_samp1a_L2_R=94.9791519744924
wl_samp1a_strLevel=-channel R -level 5.01453560865443x94.9791519744924% -channel G -level 5.01453560865443x94.9791519744924% -channel B -level 5.01453560865443x94.9791519744924% +channel```

The parameters are the same for all channels, so we could use any of them.

As a test, we find what "-level" transforms an image to itself:

`call %PICTBAT%whatLevel toes.png toes.png wl_same`

List the parameters:

`set wl_same `
```wl_same_L1_B=0
wl_same_L1_G=0
wl_same_L1_R=0
wl_same_L2_B=100
wl_same_L2_G=100
wl_same_L2_R=100
wl_same_strLevel=-channel R -level 0x100% -channel G -level 0x100% -channel B -level 0x100% +channel```

For another example, we use a negative slope:

 Negative slope: ```%IMG7%magick ^ toes.png ^ -level 95,5%% ^ wl_neg.png```

Calculate the parameters:

`call %PICTBAT%whatLevel toes.png wl_neg.png wl_neg`

List the parameters:

`set wl_neg `
```wl_neg_L1_B=94.9977015776045
wl_neg_L1_G=95.0000762951082
wl_neg_L1_R=95.0064931851386
wl_neg_L2_B=5.0007731451275
wl_neg_L2_G=5.0000762951105
wl_neg_L2_R=4.9968406754855
wl_neg_strLevel=-channel R -level 95.0064931851386x4.9968406754855% -channel G -level 95.0000762951082x5.0000762951105% -channel B -level 94.9977015776045x5.0007731451275% +channel```

If the transformation was actually -level, then just two pixels will find the parameters. But if the transformation was more complex, two arbitrary pixels will probably not find the best fit.

Script whatLevelP.bat

The script whatLevelP.bat finds the parameters for "+level-colors" or "+level".

Since the first version of this page, IM has acquired the ftxt: coder, which simplifies the process.

Suppose we had "+level L1,L2" where L1 and L2 are proportions of QuantumRange, [0..1]. The linear transformation is:

`vout = L1 + vin * (L2-L1)`

... where vin and vout are [0..1].

In each pair of pixels, we number them 0 and 1. So we have two simultaneous equations:

```vout0 = L1 + vin0 * (L2-L1)
vout1 = L1 + vin1 * (L2-L1)```

By subtraction:

`vout0 - vout1 = (vin0 - vin1) * (L2-L1)`

So:

```(L2-L1) = (vout0 - vout1) / (vin0 - vin1)

L1 = vout0 - vin0 * (L2-L1)

L2 = (L2-L1) + L1```

Thus, we can calculate L1 and L2 for each channel.

The script whatLevelP.bat does the work. It writes two environment variables that can be used in "+level-colors". It could be modified to write six environment variables, to use in a "+level" for each channel.

Which pixels?

The scripts whatLevel.bat and whatLevelP.bat, by default, use pixel values from the top-left and bottom-right corners of the images. If these pixels have integer values that are close together, small quantization errors can cause a large error in the calculated slopes. The script twoNotClipped.bat chooses pixel coordinates to minimize this problem.

Linear regression

This models the transformation from input pixel values X to output pixel values Y as:

`Y = intercept + slope * X`

... where:

```slope =    nPixels * sumXY - sigX * sigY
nPixels * sumXsquared - sigX * sigX

intercept = meanY - slope * meanX```

where, taking X and Y as zero to one:

• nPixels is the width*height in pixels.
• sumXY is nPixels * mean(X*Y).
• sigX is nPixels * meanX.
• sigY is nPixels * meanY.
• sumXsquared is nPixels * mean(X*X).
• meanY is the mean of image X, ie scaled to 1x1 pixel.
• meanX is the mean of image Y, ie scaled to 1x1 pixel.

The slope equation simplifies to:

```slope = nPixels*nPixels * (mean(X*Y) - meanX*meanY)
nPixels*nPixels * (mean(X*X) - meanX*meanX)

= mean(X*Y) - meanX*meanY
mean(X*X) - meanX*meanX```

The script linReg2img.bat calculates the slope and intercept for three channels.

`call %PICTBAT%linReg2img toes.png wl_samp1.png wl_lr1`

List the parameters:

`set wl_lr1 `
```wl_lr1_bias_B=-0.0555442404345006
wl_lr1_bias_G=-0.0555539263370718
wl_lr1_bias_R=-0.0546464913786526
wl_lr1_gain_B=1.11108997100786
wl_lr1_gain_G=1.11110952162966
wl_lr1_gain_R=1.10916304264897```

We could make all Y pixels that might have been clipped transparent.

Converting between gain-and-bias and levels

`call %PICTBAT%gbToL1L2 wl_lr1`
`set wl_lr1 `
```wl_lr1_bias_B=-0.0555442404345006
wl_lr1_bias_G=-0.0555539263370718
wl_lr1_bias_R=-0.0546464913786526
wl_lr1_gain_B=1.11108997100786
wl_lr1_gain_G=1.11110952162966
wl_lr1_gain_R=1.10916304264897
wl_lr1_L1_B=4.99907675200388
wl_lr1_L1_G=4.99986052280346
wl_lr1_L1_R=4.92682223238727
wl_lr1_L2_B=95.0007891329471
wl_lr1_L2_G=94.9999892709852
wl_lr1_L2_R=95.0848929170848```
`call %PICTBAT%gbToL3pL4p wl_lr1`
`set wl_lr1 `
```wl_lr1_bias_B=-0.0555442404345006
wl_lr1_bias_G=-0.0555539263370718
wl_lr1_bias_R=-0.0546464913786526
wl_lr1_gain_B=1.11108997100786
wl_lr1_gain_G=1.11110952162966
wl_lr1_gain_R=1.10916304264897
wl_lr1_L1_B=4.99907675200388
wl_lr1_L1_G=4.99986052280346
wl_lr1_L1_R=4.92682223238727
wl_lr1_L2_B=95.0007891329471
wl_lr1_L2_G=94.9999892709852
wl_lr1_L2_R=95.0848929170848
wl_lr1_L3p_B=-5.55442404345006
wl_lr1_L3p_G=-5.55539263370718
wl_lr1_L3p_R=-5.46464913786526
wl_lr1_L4p_B=105.554573057336
wl_lr1_L4p_G=105.555559529259
wl_lr1_L4p_R=105.451655127032```
`call %PICTBAT%L1L2toGb wl_lr1`
`set wl_lr1 `
```wl_lr1_bias_B=-0.0555442404345006
wl_lr1_bias_Bx=-0.0555442404345006
wl_lr1_bias_G=-0.0555539263370718
wl_lr1_bias_Gx=-0.0555539263370717
wl_lr1_bias_R=-0.0546464913786526
wl_lr1_bias_Rx=-0.0546464913786525
wl_lr1_gain_B=1.11108997100786
wl_lr1_gain_Bx=1.11108997100786
wl_lr1_gain_G=1.11110952162966
wl_lr1_gain_Gx=1.11110952162966
wl_lr1_gain_R=1.10916304264897
wl_lr1_gain_Rx=1.10916304264897
wl_lr1_L1_B=4.99907675200388
wl_lr1_L1_G=4.99986052280346
wl_lr1_L1_R=4.92682223238727
wl_lr1_L2_B=95.0007891329471
wl_lr1_L2_G=94.9999892709852
wl_lr1_L2_R=95.0848929170848
wl_lr1_L3p_B=-5.55442404345006
wl_lr1_L3p_G=-5.55539263370718
wl_lr1_L3p_R=-5.46464913786526
wl_lr1_L4p_B=105.554573057336
wl_lr1_L4p_G=105.555559529259
wl_lr1_L4p_R=105.451655127032```
`call %PICTBAT%L3pL4ptoGb wl_lr1`
`set wl_lr1 `
```wl_lr1_bias_B=-0.0555442404345006
wl_lr1_bias_Bx=-0.0555442404345006
wl_lr1_bias_By=-0.0555442404345006
wl_lr1_bias_G=-0.0555539263370718
wl_lr1_bias_Gx=-0.0555539263370717
wl_lr1_bias_Gy=-0.0555539263370718
wl_lr1_bias_R=-0.0546464913786526
wl_lr1_bias_Rx=-0.0546464913786525
wl_lr1_bias_Ry=-0.0546464913786526
wl_lr1_gain_B=1.11108997100786
wl_lr1_gain_Bx=1.11108997100786
wl_lr1_gain_G=1.11110952162966
wl_lr1_gain_Gx=1.11110952162966
wl_lr1_gain_R=1.10916304264897
wl_lr1_gain_Rx=1.10916304264897
wl_lr1_L1_B=4.99907675200388
wl_lr1_L1_G=4.99986052280346
wl_lr1_L1_R=4.92682223238727
wl_lr1_L2_B=95.0007891329471
wl_lr1_L2_G=94.9999892709852
wl_lr1_L2_R=95.0848929170848
wl_lr1_L3p_B=-5.55442404345006
wl_lr1_L3p_G=-5.55539263370718
wl_lr1_L3p_R=-5.46464913786526
wl_lr1_L4p_B=105.554573057336
wl_lr1_L4p_G=105.555559529259
wl_lr1_L4p_R=105.451655127032```

Scripts

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

whatLevel.bat

```rem Given image %1 was transformed by "-level L1xL2",
rem and we have the same-size result image %2,
rem what are the parameters L1 and L2?
rem Write parameters to environment variables prefixed with %3.
rem %4 quoted x,y coords of pixel in both images.
rem %5 quoted x,y coords of another pixel in both images.
@rem
@rem Also uses:
@rem   wlPreProc  Pre-process for both inputs.
@rem

rem We compare corresponding pixels in the two images.
rem The pixels must be different, and must not have been clamped.

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

@setlocal enabledelayedexpansion

@call echoOffSave

call %PICTBAT%setInOut %1 wl

set In1=%1
set In2=%2

set Coords1=%4
if [%Coords1%]==[.] set Coords1=
if [%Coords1%]==[] set Coords1="0,0"

set Coords2=%5
if [%Coords2%]==[.] set Coords2=
if [%Coords2%]==[] set Coords2="-1,-1"

call %UTIL%\parseCommaList %Coords1% Cds1 Cds1
if not %Cds1%==2 (
echo %0: Coords1 [%Coords1%] should be two comma-separated numbers.
exit /B 1
)
call %UTIL%\parseCommaList %Coords2% Cds2 Cds2
if not %Cds2%==2 (
echo %0: Coords2 [%Coords2%] should be two comma-separated numbers.
exit /B 1
)

set X1=%Cds1[0]%
set Y1=%Cds1[1]%
set X2=%Cds2[0]%
set Y2=%Cds1[1]%

for /F "usebackq" %%L in (`%IMG7%magick ^
-precision 15 ^
%In1% %In2% ^
%wlPreProc% ^
^( -clone 0-1 ^
-crop "1x1+%%[fx:mod(%X1%,w)]+%%[fx:mod(%Y1%,h)]" +repage ^
^( -clone 0 ^
-format "vin0r=%%[fx:mean.r]\nvin0g=%%[fx:mean.g]\nvin0b=%%[fx:mean.b]\n" -write info: ^
+delete ^
^) ^
^( -clone 1 ^
-format "vout0r=%%[fx:mean.r]\nvout0g=%%[fx:mean.g]\nvout0b=%%[fx:mean.b]\n" -write info: ^
+delete ^
^) ^
-delete 0--1 ^
^) ^
^( -clone 0-1 ^
-crop "1x1+%%[fx:mod(%X2%,w)]+%%[fx:mod(%Y2%,h)]" +repage ^
^( -clone 0 ^
-format "vin1r=%%[fx:mean.r]\nvin1g=%%[fx:mean.g]\nvin1b=%%[fx:mean.b]\n" -write info: ^
+delete ^
^) ^
^( -clone 1 ^
-format "vout1r=%%[fx:mean.r]\nvout1g=%%[fx:mean.g]\nvout1b=%%[fx:mean.b]\n" -write info: ^
+delete ^
^) ^
-delete 0--1 ^
^) ^
null:`) do set %%L

echo %0: vin0: %vin0r% %vin0g% %vin0b%
echo %0: vout0: %vout0r% %vout0g% %vout0b%
echo %0: vin1: %vin1r% %vin1g% %vin1b%
echo %0: vout1: %vout1r% %vout1g% %vout1b%

set FMT=^
vindr=%%[fx:(%vin0r%-(%vin1r%))]\n^
vindg=%%[fx:(%vin0g%-(%vin1g%))]\n^
vindb=%%[fx:(%vin0b%-(%vin1b%))]\n^
voutdr=%%[fx:(%vout0r%-(%vout1r%))]\n^
voutdg=%%[fx:(%vout0g%-(%vout1g%))]\n^
voutdb=%%[fx:(%vout0b%-(%vout1b%))]\n

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

echo %0: vind: %vindr% %vindg% %vindb%
echo %0: voutd: %voutdr% %voutdg% %voutdb%

if %voutdr%==0 (
echo voutdr is zero
exit /B 1
)
if %voutdg%==0 (
echo voutdg is zero
exit /B 1
)
if %voutdb%==0 (
echo voutdb is zero
exit /B 1
)

set FMT=^
L2mL1r=%%[fx:100*(%vin0r%-(%vin1r%))/(%vout0r%-(%vout1r%))]\n^
L2mL1g=%%[fx:100*(%vin0g%-(%vin1g%))/(%vout0g%-(%vout1g%))]\n^
L2mL1b=%%[fx:100*(%vin0b%-(%vin1b%))/(%vout0b%-(%vout1b%))]\n

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

echo %0: L2mL1: %L2mL1r% %L2mL1g% %L2mL1b%

set FMT=^
L1r=%%[fx:(%vin0r%*100-(%vout0r%)*(%L2mL1r%))]\n^
L1g=%%[fx:(%vin0g%*100-(%vout0g%)*(%L2mL1g%))]\n^
L1b=%%[fx:(%vin0b%*100-(%vout0b%)*(%L2mL1b%))]\n

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

echo %0: L1: %L1r% %L1g% %L1b%

set FMT=^
L2r=%%[fx:(%L2mL1r%+(%L1r%))]\n^
L2g=%%[fx:(%L2mL1g%+(%L1g%))]\n^
L2b=%%[fx:(%L2mL1b%+(%L1b%))]\n

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

echo %0: L2: %L2r% %L2g% %L2b%

set strLevel=^
-channel R -level %L1r%x%L2r%%% ^
-channel G -level %L1g%x%L2g%%% ^
-channel B -level %L1b%x%L2b%%% ^
+channel

echo %0: strLevel: %strLevel%

rem Verify: if we level with these numbers, do we get the expected result?

%IMG7%magick ^
%In1% ^
%strLevel% ^
%In2% ^
-metric RMSE ^
-format "%%[distortion]\n" -compare ^
info:

call echoRestore

@endlocal & set %3_L1_R=%L1r%& set %3_L1_G=%L1g%& set %3_L1_B=%L1b%&^
set %3_L2_R=%L2r%& set %3_L2_G=%L2g%& set %3_L2_B=%L2b%&^
set %3_strLevel=%strLevel%
```

whatLevelP.bat

```rem Given image %1 was transformed by "+level L1xL2",
rem and we have the same-size result image %2,
rem what are the parameters L1 and L2?
rem Write parameters to environment variables prefixed with %3.
rem %4 quoted x,y coords of pixel in both images.
rem %5 quoted x,y coords of another pixel in both images.
@rem
@rem Also uses:
@rem   wlPreProc  Pre-process for both inputs.
@rem

rem We compare corresponding pixels in the two images.
rem The pixels must be different, and must not have been clamped.

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

@setlocal enabledelayedexpansion

rem @call echoOffSave

call %PICTBAT%setInOut %1 wl

set In1=%1
set In2=%2

set Coords1=%4
if [%Coords1%]==[.] set Coords1=
if [%Coords1%]==[] set Coords1="0,0"

set Coords2=%5
if [%Coords2%]==[.] set Coords2=
if [%Coords2%]==[] set Coords2="-1,-1"

call %UTIL%\parseCommaList %Coords1% Cds1 Cds1
if not %Cds1%==2 (
echo %0: Coords1 [%Coords1%] should be two comma-separated numbers.
exit /B 1
)
call %UTIL%\parseCommaList %Coords2% Cds2 Cds2
if not %Cds2%==2 (
echo %0: Coords2 [%Coords2%] should be two comma-separated numbers.
exit /B 1
)

set X1=%Cds1[0]%
set Y1=%Cds1[1]%
set X2=%Cds2[0]%
set Y2=%Cds1[1]%

for /F "usebackq" %%L in (`%IMG7%magick ^
-precision 15 ^
-define "compose:clamp=off" ^
%In1% %In2% ^
%wlPreProc% ^
^( -clone 0-1 ^
-crop "1x1+%%[fx:mod(%X1%,w)]+%%[fx:mod(%Y1%,h)]" +repage ^
^( -clone 0 ^
-define ftxt:format^="vin0=\s\n" -write ftxt: ^
-write mpr:vin0 ^
+delete ^
^) ^
^( -clone 1 ^
-define ftxt:format^="vout0=\s\n" -write ftxt: ^
-write mpr:vout0 ^
+delete ^
^) ^
-delete 0--1 ^
^) ^
^( -clone 0-1 ^
-crop "1x1+%%[fx:mod(%X2%,w)]+%%[fx:mod(%Y2%,h)]" +repage ^
^( -clone 0 ^
-define ftxt:format^="vin1=\s\n" -write ftxt: ^
-write mpr:vin1 ^
+delete ^
^) ^
^( -clone 1 ^
-define ftxt:format^="vout1=\s\n" -write ftxt: ^
-write mpr:vout1 ^
+delete ^
^) ^
-delete 0--1 ^
^) ^
-delete 0--1 ^
mpr:vout0 mpr:vout1 ^
-compose MinusSrc -composite ^
^( mpr:vin0 mpr:vin1 ^
-compose MinusSrc -composite ^
^) ^
-compose DivideSrc -composite ^
-define ftxt:format^="L2mL1=\s\n" -write ftxt: ^
+write mpr:L2mL1 ^
mpr:vin0 ^
-compose Multiply -composite ^
mpr:vout0 ^
-compose MinusDst -composite ^
-define ftxt:format^="L1=\s\n" -write ftxt: ^
+write mpr:L1 ^
mpr:L2mL1 ^
-compose Plus -composite ^
-define ftxt:format^="L2=\s\n" -write ftxt: ^
null:`) do set %%L

echo %0: vin0: %vin0%
echo %0: vout0: %vout0%
echo %0: vin1: %vin1%
echo %0: vout1: %vout1%

exit /B 0

set FMT=^
vindr=%%[fx:(%vin0r%-(%vin1r%))]\n^
vindg=%%[fx:(%vin0g%-(%vin1g%))]\n^
vindb=%%[fx:(%vin0b%-(%vin1b%))]\n^
voutdr=%%[fx:(%vout0r%-(%vout1r%))]\n^
voutdg=%%[fx:(%vout0g%-(%vout1g%))]\n^
voutdb=%%[fx:(%vout0b%-(%vout1b%))]\n

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

echo %0: vind: %vindr% %vindg% %vindb%
echo %0: voutd: %voutdr% %voutdg% %voutdb%

if %voutdr%==0 (
echo voutdr is zero
exit /B 1
)
if %voutdg%==0 (
echo voutdg is zero
exit /B 1
)
if %voutdb%==0 (
echo voutdb is zero
exit /B 1
)

set FMT=^
L2mL1r=%%[fx:100*(%vin0r%-(%vin1r%))/(%vout0r%-(%vout1r%))]\n^
L2mL1g=%%[fx:100*(%vin0g%-(%vin1g%))/(%vout0g%-(%vout1g%))]\n^
L2mL1b=%%[fx:100*(%vin0b%-(%vin1b%))/(%vout0b%-(%vout1b%))]\n

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

echo %0: L2mL1: %L2mL1r% %L2mL1g% %L2mL1b%

set FMT=^
L1r=%%[fx:(%vin0r%*100-(%vout0r%)*(%L2mL1r%))]\n^
L1g=%%[fx:(%vin0g%*100-(%vout0g%)*(%L2mL1g%))]\n^
L1b=%%[fx:(%vin0b%*100-(%vout0b%)*(%L2mL1b%))]\n

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

echo %0: L1: %L1r% %L1g% %L1b%

set FMT=^
L2r=%%[fx:(%L2mL1r%+(%L1r%))]\n^
L2g=%%[fx:(%L2mL1g%+(%L1g%))]\n^
L2b=%%[fx:(%L2mL1b%+(%L1b%))]\n

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

echo %0: L2: %L2r% %L2g% %L2b%

set strLevel=^
-channel R -level %L1r%x%L2r%%% ^
-channel G -level %L1g%x%L2g%%% ^
-channel B -level %L1b%x%L2b%%% ^
+channel

echo %0: strLevel: %strLevel%

rem Verify: if we level with these numbers, do we get the expected result?

%IMG7%magick ^
%In1% ^
%strLevel% ^
%In2% ^
-metric RMSE ^
-format "%%[distortion]\n" -compare ^
info:

call echoRestore

@endlocal & set %3_L1_R=%L1r%& set %3_L1_G=%L1g%& set %3_L1_B=%L1b%&^
set %3_L2_R=%L2r%& set %3_L2_G=%L2g%& set %3_L2_B=%L2b%&^
set %3_strLevel=%strLevel%
```

linReg2img.bat

```rem Linear regression of 2 images.
rem %1 image: independent variable
rem %2 image: dependent variable
rem %3 prefix for output environment variables. Returns %3_gain_R, %3_bias_R etc.
@rem
@rem Images must be same size. (Script doesn't check this.)
@rem

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

@call echoOffSave

for /F "usebackq" %%L in (`%IMG7%magick ^
%1 ^
%2 ^
-precision 15 ^
-define "compose:clamp=off" ^
^( -clone 0 ^
-scale "1x1^!" ^
-write mpr:MnX ^
-evaluate Pow 2 ^
-write mpr:MnXMnX ^
+delete ^
^) ^
^( -clone 0 ^
-evaluate Pow 2 ^
-scale "1x1^!" ^
-write mpr:MnXX ^
+delete ^
^) ^
^( -clone 1 ^
-scale "1x1^!" ^
-write mpr:MnY ^
+delete ^
^) ^
^( -clone 0-1 ^
-compose Multiply -composite ^
-scale "1x1^!" ^
-write mpr:MnXY ^
+delete ^
^) ^
-delete 0-1 ^
^( mpr:MnX mpr:MnY ^
-compose Multiply -composite ^
mpr:MnXY ^
-compose Mathematics -define "compose:args=0,1,-1,0" -composite ^
-write mpr:NUMER ^
+delete ^
^) ^
^( mpr:MnXX mpr:MnXMnX ^
-compose Mathematics -define "compose:args=0,-1,1,0" -composite ^
mpr:NUMER ^
-compose DivideDst -composite ^
-write mpr:SLOPE ^
-format "%3_gain_R=%%[fx:mean.r]\n%3_gain_G=%%[fx:mean.g]\n%3_gain_B=%%[fx:mean.b]\n" ^
-write info: ^
+delete ^
^) ^
^( mpr:SLOPE mpr:MnX ^
-compose Multiply -composite ^
mpr:MnY ^
-compose Mathematics -define "compose:args=0,1,-1,0" -composite ^
-write mpr:INTR ^
-format "%3_bias_R=%%[fx:mean.r]\n%3_bias_G=%%[fx:mean.g]\n%3_bias_B=%%[fx:mean.b]\n" ^
-write info: ^
+delete ^
^) ^
NULL:`) do set %%L

call echoRestore
```

twoNotClipped.bat

```rem From input image %1
rem find two non-clipped pixels that are distant colorimetrically.
rem
rem Specifically: find the pixel that has the highest channel closest to 90%,
rem and the lowest channel closest to 10%.

setlocal enabledelayedexpansion

set loX=

for /F "usebackq tokens=1,4,5 delims=:(), " %%A in (`%IMG7%magick ^
%1 ^
^( +clone ^
-channel RGB -separate +channel -evaluate-sequence Min ^
-range-threshold "0,10,10,100%%" ^
-define "identify:locate=maximum" -define "identify:limit=1" ^
+write info: ^
+delete ^
^) ^
-channel RGB -separate +channel -evaluate-sequence Max ^
-range-threshold "0,90,90,100%%" ^
-define "identify:locate=maximum" -define "identify:limit=1" ^
info:`) do (
if /I "%%A"=="Red" (
echo %%A %%B %%C
if "!loX!"=="" (
set loX=%%B
set loY=%%C
) else (
set hiX=%%B
set hiY=%%C
)
)
)

echo loX=%loX% loY=%loY% hiX=%hiX% hiY=%hiY%

endlocal & set loX=%loX%& set loY=%loY%& set hiX=%hiX%& set hiY=%hiY%```

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

`%IMG7%magick -version`
```Version: ImageMagick 7.1.1-15 Q16-HDRI x64 a0a5f3d:20230730 https://imagemagick.org
Copyright: (C) 1999 ImageMagick Studio LLC
Features: Cipher DPC HDRI OpenCL OpenMP(2.0)
Delegates (built-in): bzlib cairo freetype gslib heic jng jp2 jpeg jxl lcms lqr lzma openexr pangocairo png ps raqm raw rsvg tiff webp xml zip zlib
Compiler: Visual Studio 2022 (193532217)```

To improve internet download speeds, some images may have been automatically converted (by ImageMagick, of course) from PNG or TIFF or MIFF to JPG.

Source file for this web page is whatlev.h1. To re-create this web page, execute "procH1 whatlev".

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.