snibgo's ImageMagick pages

Set mean and stddev

Setting lightness and contrast (and colour) without clipping, using power and sigmoidal-contrast.

The Gain and bias page shows a method for adjusting the mean and standard deviation (lightness and contrast) of an image. The required adjustment is easy to calculate, and easy to apply with a multiplication and addition. Sadly, that method can cause clipping, where pixel values that were within 0 to 100% have results outside that range. When only some channels are clipped, this creates horrible colour shifts.

An alternative, shown on this page and implemented as a process module, is to use "-evaluate pow" to adjust the mean and "-sigmoidal-contrast" to adjust the standard deviation (SD). Neither operation can cause clipping. (With quantum rounding, both operations can cause values to become 0 or 100%, but values are never pushed beyond those limits.) However, there are two difficulties:

  1. There is no obvious way to calculate the parameters for the two operations.
  2. Both operations change both the mean and the SD.

We solve both problems with iteration.

See Process modules: set mean and stddev for the source code.

Sample input

set SRC=toes.png
toes.pngjpg

The module

The module setmnsd processes all the images in the current list, trying to set each to a specified mean and standard deviation. It takes the following options:

Option Description
Short
form
Long form
mn N meanGoal N Goal for the mean.
Proportion of quantum 0.0 to 1.0, or percentage suffixed by 'c' or '%', or pin.
Default: no goal for mean value.
sd N sdGoal N Goal for standard deviation.
Proportion of quantum 0.0 to 0.5, or percentage suffixed by 'c' or '%', or pin.
Default: no goal for SD.
t N tolerance N Tolerance for mean and standard deviation.
Proportion of quantum, or percentage suffixed by 'c' or '%'.
Default: 0.00001 (0.001%).
m N mid N Mid-point for sigmoidal-contrast, as percentage of quantum (0.0 to 100.0)
or mean to use the mean of the image for the mid-point.
Default: mean.
i0 N initCon0 N Lower initial guess for contrast.
Can be 0.0.
Default: 0.0000001.
i2 N initCon2 N Upper initial guess for contrast.
Default: 30.
d string direction string One of:
    incOnly to prevent the SD decreasing;
    decOnly to prevent the SD increasing;
or both for no restriction.
Default: both.
f string file string Write verbose text to stderr or stdout.
Default: stderr.
v verbose Write text information.
v2 verbose2 Write more text information.

We should specify a meanGoal or a sdGoal or both. If we provide neither (or pin both), the image won't change.

We will show the mean and standard deviation of each result.

set STATFMT=MN=%%[fx:mean]\nSD=%%[fx:standard_deviation]

Set a mean.

%IM7DEV%magick ^
  %SRC% ^
  -process 'setmnsd mn 0.7' ^
  +write sms_ex1.png ^
  -format "%STATFMT%" ^
  info: 
MN=0.69999952
SD=0.10907938
sms_ex1.pngjpg

Set a standard deviation.

%IM7DEV%magick ^
  %SRC% ^
  -process 'setmnsd sd 0.3' ^
  +write sms_ex2.png ^
  -format "%STATFMT%" ^
  info: 
MN=0.48697918
SD=0.30000347
sms_ex2.pngjpg

Set mean and SD.

%IM7DEV%magick ^
  %SRC% ^
  -process 'setmnsd mn 0.7 sd 0.3' ^
  +write sms_ex3.png ^
  -format "%STATFMT%" ^
  info: 
MN=0.69869321
SD=0.29193931
sms_ex3.pngjpg

Specifying pin for a goal mean or SD is equivalent to specifying that statistic, from the input image, as a goal.

Set a mean, pinning the SD.

%IM7DEV%magick ^
  %SRC% ^
  -process 'setmnsd mn 0.7 sd pin' ^
  +write sms_exp1.png ^
  -format "%STATFMT%" ^
  info: 
MN=0.6999994
SD=0.15322352
sms_exp1.pngjpg

Set a standard deviation, pinning the mean.

%IM7DEV%magick ^
  %SRC% ^
  -process 'setmnsd mn pin sd 0.3' ^
  +write sms_exp2.png ^
  -format "%STATFMT%" ^
  info: 
MN=0.48816097
SD=0.29998972
sms_exp2.pngjpg

The default tolerance is fairly tight. Web-sized images take about a second to process. Relaxing the tolerance (making it larger) will increase the speed (but reduce the accuracy).

Set a standard deviation, pinning the mean.

%IM7DEV%magick ^
  %SRC% ^
  -process 'setmnsd mn pin sd 0.3 t 0.1' ^
  +write sms_exp3.png ^
  -format "%STATFMT%" ^
  info: 
MN=0.49072629
SD=0.25778754
sms_exp3.pngjpg

As described below, initCon0 and initCon2 are initial guesses for the contrast seting of -sigmoidal-contrast. The default initial guesses work well for ordinary photos and sensible SD goals and tolerances. In extreme cases, a wider margin can be given, eg "initCon0 0 initCon2 1000".

The verbose option writes summmary text:

%IM7DEV%magick ^
  %SRC% ^
  -precision 9 ^
  -process 'setmnsd i2 1000 sd 0.3 mn 0.5 verbose' ^
  NULL: 
setmnsd options:  verbose  meanGoal 0.5  sdGoal 0.3  tolerance 1e-05  mid mean  initCon0 1e-07  initCon2 1000  direction both
input: mean=0.488162089 sd=0.153220962
nIter=56
result: mean=0.499998865  sd=0.299994541
setmnsd command: -sigmoidal-contrast 12.4828421,48.8162089% -evaluate pow 0.945598623

The verbose2 option writes the above, plus text at each iteration.

The module writes the two operations as a property to the image. Then images in a sequence, such as frames of a video clip, can be processed identically.

%IM7DEV%magick ^
  %SRC% ^
  -precision 19 ^
  -process 'setmnsd i2 1000 sd 0.3 mn 0.5 mid mean t 0.0001' ^
  -format %%[filter:setmnsd] info: 
-sigmoidal-contrast 12.46531119256544606,48.81620885772588281% -evaluate pow 0.9457281350827180244

The operations are written as properties in output files, so we can obtain them later:

%IMDEV%identify ^
  -format %%[filter:setmnsd] ^
  sms_exp3.png 
-sigmoidal-contrast 8.8572964,48.816209% -evaluate pow 1.0073595

If you don't want the operations written to the output file, use +define filter:setmnsd after calling the module and before writing the file.

If no goals were set, the recorded operation is "-evaluate Add 0".

The process uses the IM function GetImageMean() which calculates overall mean and standard deviation, ignoring the alpha channel, so this module should not be used for images that have any transparency. I may add a "regardalpha" facility for this.

The module uses statistics from all colour channels, and adjusts all channels by the same sigmoidal contrast.

If the SD of the input is zero no operations can change the SD. If a goal SD was set, a warning is issued and the goal is ignored. (However, IM sometimes assigns a small but non-zero SD to constant-colour images.)

If the mean of the input is less than or equal to zero, or equal to 100%, no operations can change the mean. If a goal mean was set, a warning is issued and the goal is ignored.

Transfer curves

A transfer curve (or transformation curve) shows the effect of the operations graphically. The x-axis represents the input value in a channel, from 0 to 100%. The y-axis represents the output in that channel.

The script smsCurve.bat takes an image created by the process module, extracts the property, echoes it, makes a clut from the operations, and turns this into a graph.

call %PICTBAT%smsCurve sms_ex1.png 
 -evaluate pow 0.47821176
F:\pictures\graph1d: WW=256 HH=1 newH=256 OUTFILE=f:\prose\PICTURES\sms_ex1_smsc.png
sms_ex1_smsc.pngjpg
call %PICTBAT%smsCurve sms_ex2.png 
-sigmoidal-contrast 12.192545,48.816209%
F:\pictures\graph1d: WW=256 HH=1 newH=256 OUTFILE=f:\prose\PICTURES\sms_ex2_smsc.png
sms_ex2_smsc.pngjpg
call %PICTBAT%smsCurve sms_ex3.png 
-sigmoidal-contrast 30,48.816209% -evaluate pow 0.24977812
F:\pictures\graph1d: WW=256 HH=1 newH=256 OUTFILE=f:\prose\PICTURES\sms_ex3_smsc.png
sms_ex3_smsc.pngjpg
call %PICTBAT%smsCurve sms_exp1.png 
-sigmoidal-contrast 5.8424954,48.816209% -evaluate pow 0.46376709
F:\pictures\graph1d: WW=256 HH=1 newH=256 OUTFILE=f:\prose\PICTURES\sms_exp1_smsc.png
sms_exp1_smsc.pngjpg
call %PICTBAT%smsCurve sms_exp2.png 
-sigmoidal-contrast 12.216172,48.816209% -evaluate pow 0.99498358
F:\pictures\graph1d: WW=256 HH=1 newH=256 OUTFILE=f:\prose\PICTURES\sms_exp2_smsc.png
sms_exp2_smsc.pngjpg
call %PICTBAT%smsCurve sms_exp3.png 
-sigmoidal-contrast 8.8572964,48.816209% -evaluate pow 1.0073595
F:\pictures\graph1d: WW=256 HH=1 newH=256 OUTFILE=f:\prose\PICTURES\sms_exp3_smsc.png
sms_exp3_smsc.pngjpg

The curves always start at (0,0), bottom-left, and end at (100%,100%), top-right. Some ranges of input decrease in contrast, while other ranges increase in contrast.

How does the process module work?

This section first describes how we can separately adjust the mean and the standard deviation. Then it describes how we adjust the two together.

Adjusting the mean

We raise pixel values to a power p so that values at current_mean become goalMn:

(current_mean)p = goalMn

Rearranging, p is the log of the goal mean divided by the log of the current mean.

p = log (goalMn) / log (curent_mean)

However, this isn't quite what we want. For example, suppose we want a new mean of 0.7:

%IMG7%magick ^
  toes.png ^
  -evaluate pow %%[fx:log(0.7)/log(mean)] ^
  -format %%[fx:mean] ^
  +write sms_adjmn1.png ^
  info: 
0.69044

The formula will certainly adjust so that any pixel values that happened to be at the mean will become 0.7. But this doesn't imply that the mean of the entire image will become 0.7. However, it has got us closer. We can repeat, using the previous image:

%IMG7%magick ^
  sms_adjmn1.png ^
  -evaluate pow %%[fx:log(0.7)/log(mean)] ^
  -format %%[fx:mean] ^
  +write sms_adjmn2.png ^
  info: 
0.699646

This is nearly the required result. We can keep iterating until we are within the required tolerance, which happens quickly. Where p1, p2, p3 ... are the results of the division of the logs, pixel values have changed:

v' = ((vp1)p2)p3...

or:

v' = v(p1*p2*p3...)

So we calculate P, the overall exponent we need to change the image for the required mean, as the product of the exponents of the iterations.

P = p1*p2*p3...

The operation -evaluate Pow P will generally change the SD as well as the mean. The effect on the SD is not easily predictable.

Adjusting the SD

In its "-" form, the operation "-sigmoidal-contrast C,M%" will push values away from the mid-point, which increases overall contrast. The "+" form has the opposite effect. M is the mid-point fo the operation, which is the value at which contrast increases the most.

For M, the image mean is often a sensible choice. (The median might be more sensible, but this takes more effort to calculate.)

For C, larger values have more effect, and smaller values have less effect. If we have a value for C that has too little effect, and another that has too much effect, then some value in between is exactly right, so we iterate until we find it.

The operation sigmoidal-contrast will generally change the mean as well as the SD. The effect on the mean is not easily predictable.

Adjusting both

The goal is to find both P and C that results in an image with the required mean and SD.

For a given value of C, we apply the "sigmoidal-contrast" and then iterate through P working towards the required mean. The contrast of the result, after applying both "sigmoidal-contrast C,M%" and "-evaluate Pow p", may be too high or too low, so we know in which direction we need to move for the next guess at C.

We start with two initial guesses at C taken from initCon0 and initCon2. If we need to decrease contrast, we invert these. Then setting C to contrast0 should give a result that has the SD too low, and contrast2 will give an SD that is too high.

We take the arithmetic or geometric mean of the two contrasts as the third guess, contrast1, and process at that setting, and either finish iterating or replace one of contrast0 or contrast2 and continue iterating.

To reduce unnecessarily precise work, the tolerance for the search of P is relaxed to the error from the previous iteration.

Both operations leave values at 0 and 100% unchanged. For example, black will stay black and white will stay white.

Neither operation will make an ordinary photograph entirely black or entirely white ("meanGoal 0" or "meanGoal 1"). The code would give results very close to these goals, but we get nan (not a number) or inf (infinity) as operation parameters, so the module deals with these goals as special cases.

Large images

The time taken is proportional to the number of pixels mutiplied by the number of iterations. Here is a large image:

set LGE_SRC=AGA_1434_gms.tiff

%IMG7%magick identify %LGE_SRC% 
AGA_1434_gms.tiff TIFF 4924x7378 4924x7378+0+0 16-bit sRGB 166.02MiB 0.000u 0:00.001

We apply the process module to the image, and time how long it takes:

%IM7DEV%magick ^
  %LGE_SRC% ^
  -process 'setmnsd mn 0.5 sd 0.166667' ^
  +write sms_lge1.miff ^
  -format "%%[filter:setmnsd]\n" ^
  info: 
-sigmoidal-contrast 7.3397445,37.056397% -evaluate pow 0.91437287
0 00:06:11

This is slow. How close is the result to what we wanted?

%IM7DEV%magick ^
  sms_lge1.miff ^
  -format "%STATFMT%" ^
  info: 
MN=0.49999959
SD=0.16666116

For ordinary photographs that contain data at low frequencies as well as high frequencies, resizing down does not change the mean and standard deviation by much, so a small version can be used as proxy to find the required sigmoidal contrast and power. This massively reduces the time for this module.

(Shrinking an ordinary photograph will change the mean by very little. High-frequency detail will get smoothed out, so if this contributes significantly to the SD, the SD will reduce.)

The script setMnSdLge.bat defines an image as "small" when both dimensions are less than or equal to 600 pixels. Small images are processed directly with the module. Large images are first resized down, and this is used as a proxy to find the operations, which are then applied to the large image.

call %PICTBAT%setMnSdLge ^
  %LGE_SRC% ^
  sms_lge2.miff ^
  "mn 0.5 sd 0.166667"

echo smslOPS=%smslOPS% 
smslOPS=-sigmoidal-contrast 7.436564877260658513,37.0564743136137551% -evaluate pow 0.916896224098582624 
0 00:00:35

This is an order of magnitude faster. How close is the result to what we wanted?

%IM7DEV%magick ^
  sms_lge2.miff ^
  -format "%STATFMT%" ^
  info: 
MN=0.49992966
SD=0.16811566

For many purposes, this is sufficiently accurate. If we needed more accuracy fairly quickly, we could then use the process module directly on the large image, but with parameters initCon0 and initCon1 closely bracketing the found value for contrast.

Comparison with gain-and-bias method

When an image has been saved in integer format, we don't know which pixels have been clipped. But we do know which pixels have 0 or 100% in any channel, and can we highlight those.

toes.png is auto-levelled; one pixel has 0 in the blue channel, and one pixel with 100% in the red channel.

The script compMnSd.bat creates images from the sigmoid-and-power method and the gain-and-bias method, and highlights pixels that have 0 or 100% in any channel.

Command Sigmoid & power Gain & bias
call %PICTBAT%compMnSd ^
  toes.png sms_comp1_ 0.7 0.3
sms_comp1__sp.pngjpg sms_comp1__gb.pngjpg
call %PICTBAT%compMnSd ^
  toes.png sms_comp2_ 0.7 pin
sms_comp2__sp.pngjpg sms_comp2__gb.pngjpg
call %PICTBAT%compMnSd ^
  toes.png sms_comp3_ pin 0.3
sms_comp3__sp.pngjpg sms_comp3__gb.pngjpg
call %PICTBAT%compMnSd ^
  toes.png sms_comp4_ 0.5 0.166667
sms_comp4__sp.pngjpg sms_comp4__gb.pngjpg

Of these two methods, the gain-and-bias method pushes more pixels to extremes.

Visually comparing non-highlighted areas, there are obvious differences. There are infinitely many transformations that will set an image to a given mean and given standard deviation.

Performance

We compare the process module with the sigSetSd.bat script.

First, time the script:

call StopWatch
call %PICTBAT%sigSetSd %SRC% mean
call StopWatch 
echo %sssOPTION% 
0 00:00:08
-sigmoidal-contrast 2.51162,48.8162089% 

Now time the process module:

call StopWatch

%IM7DEV%magick ^
  toes.png ^
  -process 'setmnsd sd 0.166667' ^
  -format "%%[filter:setmnsd]\n" ^
  info: 

call StopWatch 
-sigmoidal-contrast 2.5112469,48.816209%
0 00:00:03

The process module is significantly faster than the script.

Application: adding zing to photographs

I generally like photos to have a minimum SD of 0.166667, as set by the sigmoidal-contrast method:

%IM7DEV%magick ^
  %SRC% ^
  -process 'setmnsd sd 0.166667 direction incOnly' ^
  sms_zing.png
sms_zing.pngjpg

(There is nothing magical about 0.166667; it just seems typical of photos I like. For comparison: an image with no detail has SD=0.0; the maximum possible SD is 0.5; and a linear gradient has SD=sqrt(1/3+1/4) = 0.288.)

Application: colorizing images

Suppose we have a colour, defined as percentages of quantum for the three channels. We can set the mean of each channel of an image to be the required value.

Pick a value for each channel:

set R_PC=20
set G_PC=30
set B_PC=80

We show the colour, just for interest.

%IM7DEV%magick ^
  -size 200x200 ^
  xc:rgb(%R_PC%%%,%G_PC%%%,%B_PC%%%) ^
  sms_ci_col.png
sms_ci_col.png
%IM7DEV%magick ^
  %SRC% ^
  -channel RGB ^
  -separate ^
  ( -clone 0 ^
    -process 'setmnsd mn %R_PC%c' ^
  ) ^
  -delete 0 ^
  ( -clone 0 ^
    -process 'setmnsd mn %G_PC%c' ^
  ) ^
  -delete 0 ^
  ( -clone 0 ^
    -process 'setmnsd mn %B_PC%c' ^
  ) ^
  -delete 0 ^
  -combine ^
  sms_ci_out.png
sms_ci_out.pngjpg

Application: matching images

Like gain-and-bias, we can use statistics from one image to tweak another.

toes_holed.png

toes_holed.png

With the script meanSdTr.bat (see Gain and bias), we get mean and SD statistics for each channel:

call %PICTBAT%meanSdTr toes_holed.png SRC_TH_

Just for interest, here are the statistics:

set SRC_TH_ 
SRC_TH_mn_B=0.3673256656748302706
SRC_TH_mn_G=0.462861179856183691
SRC_TH_mn_R=0.4354760636015106434
SRC_TH_sd_B=0.07707375919117646912
SRC_TH_sd_G=0.07253559345674066849
SRC_TH_sd_R=0.09353533059624627444

With the script setMnSdRGB.bat, we apply these statistics to %SRC%, so each channel becomes the required mean and standard deviation:

call %PICTBAT%setMnSdRGB ^
  %SRC% sms_set_th.png SRC_TH_
sms_set_th.pngjpg

Another example:

Tweak this image:
dpt_lvs_sm.jpg

dpt_lvs_sm.jpg

Apply the statistics.

call %PICTBAT%setMnSdRGB ^
  dpt_lvs_sm.jpg ^
  sms_set_th2.png ^
  SRC_TH_
sms_set_th2.pngjpg

The script setMnSdRGB.bat has written the sigmoidal and power commands to environment variables, so we can apply the same commands to a gradient to get a transfer graph:

echo %smsrCMD_R% 
echo. 
echo %smsrCMD_G% 
echo. 
echo %smsrCMD_B% 
+sigmoidal-contrast 15.5143395640154349,47.73625977937084741% -evaluate pow 1.115810318831561787 
 
+sigmoidal-contrast 14.68163295356687748,32.71980915905594856% -evaluate pow 0.5793241895300004218 
 
+sigmoidal-contrast 31.76182234096728152,19.47673900946021419% -evaluate pow 0.4932331971113975144 
%IMG7%magick ^
  -size 1x256 gradient: ^
  -rotate 90 ^
  -colorspace sRGB ^
  -channel R %smsrCMD_R% ^
  -channel G %smsrCMD_G% ^
  -channel B %smsrCMD_B% ^
  +channel ^
  sms_xfer.png

call %PICTBAT%graphLineCol ^
  sms_xfer.png . 0 0
sms_xfer_glc.png

Scripts

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

For the source code of setmnsd.c, see Process modules: set mean and stddev.

smsCurve.bat

@rem Given image %1 has property filter:setmnsd,
@rem writes %2 a graph1d curve.
@rem
@rem Updated:
@rem   6-August-2022 for IM v7
@rem

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

@setlocal enabledelayedexpansion

@call echoOffSave

call %PICTBAT%setInOut %1 smsc

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

set OPS=
for /F "usebackq tokens=*" %%L in (`%IMG7%magick identify ^
  -format "OPS=%%[filter:setmnsd]" ^
  %INFILE%`) do set %%L
if ERRORLEVEL 1 exit /B 1
if "%OPS%"=="" exit /B 1

echo %OPS%

@rem Extreme operation can cause staircasing unless we use 32f.

%IM7DEV%magick ^
  -size 1x256 gradient: -rotate 90 ^
  %OPS% ^
  %OUTFILE%

call %PICTBAT%graph1d %OUTFILE% . . %OUTFILE%

call echoRestore

@endlocal & set smscOUTFILE=%OUTFILE%

setMnSdLge.bat

rem From image %1
rem makes %2
rem with process module 'setMnSd', parameters %3 eg "mn 0.5 sd 0.16667",
rem perhaps using statistics from a small version.
@rem
@rem Updated:
@rem   6-August-2022 for IM v7
@rem

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

@setlocal enabledelayedexpansion

@call echoOffSave

call %PICTBAT%setInOut %1 smsl

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

set sPARAMS=%~3

set LIM_W=600
set LIM_H=600

set WW=
for /F "usebackq" %%L in (`%IMG7%magick identify ^
  -format "WW=%%w\nHH=%%h\n" ^
  %INFILE%`) do set %%L
if "%WW%"=="" exit /B 1

set isSmall=0

if %WW% LEQ %LIM_W% if %HH% LEQ %LIM_H% set isSmall=1

if %isSmall%==1 (

  echo Small

  for /F "usebackq tokens=*" %%L in (`%IM7DEV%magick ^
    %INFILE% ^
    -process 'setmnsd %sPARAMS%' ^
    +write %OUTFILE% ^
    -format "sOPS=%%[filter:setmnsd]" info:`) do set %%L

) else (

  echo Large

  for /F "usebackq tokens=*" %%L in (`%IM7DEV%magick ^
    %INFILE% ^
    -precision 19 ^
    -resize %LIM_W%x%LIM_H% ^
    -process 'setmnsd %sPARAMS%' ^
    -format "sOPS=%%[filter:setmnsd]" info:`) do set %%L

  %IM7DEV%magick ^
    %INFILE% ^
    !sOPS! ^
    %OUTFILE%
)

echo %0: sOPS=!sOPS!

@call echoRestore

endlocal & set smslOUTFILE=%OUTFILE%& set smslOPS=%sOPS%

MnSdGb.bat

rem From image %1,
rem writes output %2
rem with mean to %3 (a number 0.0 to 1.0, or "pin")
rem and SD to %4 (a number 0.0 to 1.0, or "pin")
rem by gain-and-bias method. All channels shifted by same amount.
@rem
@rem Updated:
@rem   7-August-2022 for IM v7
@rem

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

@setlocal enabledelayedexpansion

@call echoOffSave

call %PICTBAT%setInOut %1 msg

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

set GOAL_MN=%3
if "%GOAL_MN%"=="." set GOAL_MN=
if "%GOAL_MN%"=="" set GOAL_MN=0.5

set GOAL_SD=%4
if "%GOAL_SD%"=="." set GOAL_SD=
if "%GOAL_SD%"=="" set GOAL_SD=0.16667


for /F "usebackq" %%L in (`%IMG7%magick identify ^
  -precision 19 ^
  -format "CUR_MN=%%[fx:mean]\nCUR_SD=%%[fx:standard_deviation]" ^
  %INFILE%`) do set %%L

if /I "%GOAL_SD%" EQU "pin" (
  set GAIN=1
) else (
  for /F "usebackq" %%L in (`%IMG7%magick identify ^
    -precision 19 ^
    -format "GAIN=%%[fx:%GOAL_SD%/%CUR_SD%]" ^
    xc:`) do set %%L
)

if /I "%GOAL_MN%" EQU "pin" set GOAL_MN=%CUR_MN%

  for /F "usebackq" %%L in (`%IMG7%magick identify ^
    -precision 19 ^
    -format "BIAS=%%[fx:%GOAL_MN%-%CUR_MN%*%GAIN%]" ^
    xc:`) do set %%L

%IMG7%magick ^
  %INFILE% ^
  -function Polynomial %GAIN%,%BIAS% ^
  %OUTFILE%

call echoRestore

@endlocal & set msgOUTFILE=%OUTFILE%

setMnSdRGB.bat

rem Given image %1
rem writes %2 setting mean and standard deviation
rem from variables starting with %3 (eg created by meanSdTr).
@rem
@rem Updated:
@rem   6-August-2022 for IM v7
@rem

setlocal

set STDOPT=i0 0 i2 1000

for /F "usebackq tokens=*" %%L in (`%IM7DEV%magick ^
  %1 ^
  -precision 19 ^
  -channel RGB ^
  -separate ^
  ^( -clone 0 ^
     -process 'setmnsd mn !%3mn_R! sd !%3sd_R! %STDOPT% f stdout' ^
     -format "CMD_R=%%[filter:setmnsd]\n" +write info:
  ^) ^
  -delete 0 ^
  ^( -clone 0 ^
     -process 'setmnsd mn !%3mn_G! sd !%3sd_G! %STDOPT% f stdout' ^
     -format "CMD_G=%%[filter:setmnsd]\n" +write info:
  ^) ^
  -delete 0 ^
  ^( -clone 0 ^
     -process 'setmnsd mn !%3mn_B! sd !%3sd_B! %STDOPT% f stdout' ^
     -format "CMD_B=%%[filter:setmnsd]\n" +write info:
  ^) ^
  -delete 0 ^
  -combine ^
  %2`) do set %%L

echo CMD_R=%CMD_R%
echo CMD_G=%CMD_G%
echo CMD_B=%CMD_B%

endlocal & set smsrCMD_R=%CMD_R%& set smsrCMD_G=%CMD_G%& set smsrCMD_B=%CMD_B%

compMnSd.bat

rem Compare mean and SD methods.
rem %1 input image
rem %2 output prefix
rem %3 goal mean
rem %4 goal SD
@rem
@rem Updated:
@rem   6-August-2022 for IM v7
@rem

@setlocal enabledelayedexpansion

rem Methods: sigmoid and power, gain and bias.

set INFILE=%1

set OUT_SP=%2_sp.png
set OUT_GB=%2_gb.png

set GOAL_MN=%3
set GOAL_SD=%4

%IM7DEV%magick ^
  %INFILE% ^
  -process 'setmnsd mn %GOAL_MN% sd %GOAL_SD%' ^
  %OUT_SP%

%IMG7%magick identify ^
  -format "MN=%%[fx:mean]\nSD=%%[fx:standard_deviation]" ^
  %OUT_SP%


call %PICTBAT%neg0100 %OUT_SP% %OUT_SP%


call %PICTBAT%MnSdGb ^
  %INFILE% ^
  %OUT_GB% ^
  %GOAL_MN% %GOAL_SD%

%IMG7%magick identify ^
  -format "MN=%%[fx:mean]\nSD=%%[fx:standard_deviation]" ^
  %OUT_GB%

call %PICTBAT%neg0100 %OUT_GB% %OUT_GB%

neg0100.bat

rem In R, G and B channels,
rem make values that were 0 into 100%, and make values that were 100% into 0.
@rem
@rem Updated:
@rem   6-August-2022 for IM v7
@rem

%IMG7%magick ^
  %1 ^
  -channel RGB ^
  -separate ^
  +channel ^
  -fill Red -opaque Black ^
  -fill Black -opaque White ^
  -fill White -opaque Red ^
  -combine ^
  %2

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

%IMG7%magick -version
Version: ImageMagick 7.1.0-42 Q16-HDRI x64 396d87c:20220709 https://imagemagick.org
Copyright: (C) 1999 ImageMagick Studio LLC
License: https://imagemagick.org/script/license.php
Features: Cipher DPC HDRI OpenCL 
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 (193231332)
%IM7DEV%magick -version
Version: ImageMagick 7.1.0-20 Q32-HDRI x86_64 2021-12-29 https://imagemagick.org
Copyright: (C) 1999-2021 ImageMagick Studio LLC
License: https://imagemagick.org/script/license.php
Features: Cipher DPC HDRI Modules OpenMP(4.5) 
Delegates (built-in): bzlib cairo fontconfig fpx freetype jbig jng jpeg lcms ltdl lzma pangocairo png raqm rsvg tiff webp wmf x xml zip zlib
Compiler: gcc (11.2)

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


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 20-July-2017.

Page created 07-Aug-2022 01:47:13.

Copyright © 2022 Alan Gibson.