snibgo's ImageMagick pages

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

toes.pngjpg

Increase contrast slightly:

%IMG7%magick ^
  toes.png ^
  -level 5,95%% ^
  wl_samp1.png
wl_samp1.pngjpg

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
wl_appd.pngjpg

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
wl_neg.pngjpg

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

See Wikipedia: Ordinary least squares: Simple linear regression model.

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:

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
License: https://imagemagick.org/script/license.php
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.

Anyone is permitted to link to this page, including for commercial use.


Page version v1.0 7-August-2020.

Page created 23-Oct-2023 15:26:13.

Copyright © 2023 Alan Gibson.