With four numbers, we can tweak the bottom, top and somewhere in the middle.
We will demonstrate on the usual image:
set SRC=toes.png |
The script powSmh.bat creates a transformed image, and can also create a graph that shows the transformation in the usual way, with input along the x-axis and output along the y-axis.
With no parameters, we get no transformation. call %PICTBAT%powSmh ^ %SRC% psmh_t1.png . . psmh_t1_dbg.png f:\prose\PICTURES>rem (Y0, Y1, Y2), F:\pictures\powSmh: X1 = 0.5 F:\pictures\powSmh: Y0 = 0, Y1 = 0.5, Y2 = 1 F:\pictures\powSmh: a = 1, b = 0, p = 1 F:\pictures\powSmh: xAtZero = -0 F:\pictures\powSmh: xAtOne = 1 F:\pictures\graphLineCol: S_GRID= F:\pictures\graphLineCol: sBACK=-background #000 F:\pictures\graphLineCol: sCLUT= |
|
Transform so input 0.6 becomes output 0.5 (0.6 => 0.5). call %PICTBAT%powSmh ^ %SRC% psmh_t2.png 0.6 . psmh_t2_dbg.png f:\prose\PICTURES>rem (Y0, Y1, Y2), F:\pictures\powSmh: X1 = 0.6 F:\pictures\powSmh: Y0 = 0, Y1 = 0.5, Y2 = 1 F:\pictures\powSmh: a = 1, b = 0, p = 1.356915448856724 F:\pictures\powSmh: xAtZero = 0 F:\pictures\powSmh: xAtOne = 1 F:\pictures\graphLineCol: S_GRID= F:\pictures\graphLineCol: sBACK=-background #000 F:\pictures\graphLineCol: sCLUT= |
|
Transform so 0.5 => 0.4. call %PICTBAT%powSmh ^ %SRC% psmh_t3.png . ".,0.4,." psmh_t3_dbg.png f:\prose\PICTURES>rem (Y0, Y1, Y2), F:\pictures\powSmh: X1 = 0.5 F:\pictures\powSmh: Y0 = 0, Y1 = 0.4, Y2 = 1 F:\pictures\powSmh: a = 1, b = 0, p = 1.321928094887362 F:\pictures\powSmh: xAtZero = 0 F:\pictures\powSmh: xAtOne = 1 F:\pictures\graphLineCol: S_GRID= F:\pictures\graphLineCol: sBACK=-background #000 F:\pictures\graphLineCol: sCLUT= |
|
Transform so 0.0 => 0.1, 0.5 => 0.4, 1.0 => 0.9. call %PICTBAT%powSmh ^ %SRC% psmh_t4.png . "0.1,0.4,0.9" psmh_t4_dbg.png f:\prose\PICTURES>rem (Y0, Y1, Y2), F:\pictures\powSmh: X1 = 0.5 F:\pictures\powSmh: Y0 = 0.1, Y1 = 0.4, Y2 = 0.9 F:\pictures\powSmh: a = 0.8, b = 0.1, p = 1.415037499278844 F:\pictures\powSmh: xAtZero = -nan(ind) F:\pictures\powSmh: xAtOne = 1.086799011023172 F:\pictures\graphLineCol: S_GRID= F:\pictures\graphLineCol: sBACK=-background #000 F:\pictures\graphLineCol: sCLUT= |
|
Transform so 0.0 => -0.1, 0.5 => 0.4, 1.0 => 1.1. call %PICTBAT%powSmh ^ %SRC% psmh_t5.png . "-0.1,0.4,1.1" psmh_t5_dbg.png f:\prose\PICTURES>rem (Y0, Y1, Y2), F:\pictures\powSmh: X1 = 0.5 F:\pictures\powSmh: Y0 = -0.1, Y1 = 0.4, Y2 = 1.1 F:\pictures\powSmh: a = 1.2, b = -0.1, p = 1.263034405833794 F:\pictures\powSmh: xAtZero = 0.139818503296633 F:\pictures\powSmh: xAtOne = 0.9334286593509811 F:\pictures\graphLineCol: S_GRID= F:\pictures\graphLineCol: sBACK=-background #000 F:\pictures\graphLineCol: sCLUT= |
|
We can have negative transformations, where y0 > y1 > y2:
call %PICTBAT%powSmh ^ %SRC% psmh_t6.png . "1.1,0.4,-0.1" psmh_t6_dbg.png f:\prose\PICTURES>rem (Y0, Y1, Y2), F:\pictures\powSmh: X1 = 0.5 F:\pictures\powSmh: Y0 = 1.1, Y1 = 0.4, Y2 = -0.1 F:\pictures\powSmh: a = -1.2, b = 1.1, p = 0.7776075786635519 F:\pictures\powSmh: xAtZero = 0.8941370136942888 F:\pictures\powSmh: xAtOne = 0.04094280203134158 F:\pictures\graphLineCol: S_GRID= F:\pictures\graphLineCol: sBACK=-background #000 F:\pictures\graphLineCol: sCLUT= |
|
The general equation for output pixel values y from input pixel values x is:
y = a*xp + b
The algorithm is given three values of input x, and corresponding values of output y.
The three parameters p, a and b are calculated. Two of them are easy:
a = Y2 - Y0 b = Y0
Substituting into the general equation:
Y1 = (Y2 - Y0) * (X1)p + Y0
Rearranging:
(X1)p = (Y1 - Y0) / (Y2 - Y0) p = log [(Y1 - Y0) / (Y2 - Y0)] log (X1)
Now we have parameter p for "-evaluate Pow", and parameters a and b for "-function Polynomial".
To calculate where the line intersects y=0 and y=1:
y = a*xp + b xp = (y - b) / a x = ((y - b) / a)1/p
So, at y=0:
xatZero = (-b / a)1/p
And, at y=1:
xatOne = ((1 - b) / a)1/p
When -b or 1-b is negative, there is no intersection.
For the common case when Y0=0 and Y1=1, then a=1, b=0 and p=log(Y1)/log(X1).
The method shown on this page is very similar to Lift, Gamma, Gain functions in DaVinci Resolve and similar programs. However, in those programs the user tweaks the parameters that I call a, p and b. Changing any one of these parameters changes the entire image somewhat, though mostly at the bottom, middle and top respectively. So the user will often repeatedly adjust all three controls to get a desired effect.
Lift and gain correspond to TV or video monitor controls brightness and contrast.
There are many varieties of this concept, such as the ASC CDL (American Society of Cinematographers Color Decision List, see Wikipedia):
y = (x * slope + offset)power
In my version, the user directly supplies the three desired outputs for the bottom, middle and top, and the program calculates values for a, p and b.
For convenience, .bat scripts are also available in a single zip file. See Zipped BAT files.
@rem Given %1 is an image, @rem %2 is output file, @rem %3 is an input mid-value, (X1) @rem %4 is a set of quoted output shadow, mid and highlight values rem (Y0, Y1, Y2), @rem all values typically 0.0 to 1.0. @rem %5 if a filename, also writes debugging graph image. @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 psmh if not "%2"=="" if not "%2"=="." set OUTFILE=%2 set X1=%3 if "%X1%"=="." set X1= if "%X1%"=="" set X1=0.5 set OUTVALS=%~4 if "%OUTVALS%"=="." set OUTVALS= if "%OUTVALS%"=="" set OUTVALS=0,0.5,1.0 set DEBUG=%5 if "%DEBUG%"=="." set DEBUG= for /F "tokens=1-3 delims=, " %%A in ("%OUTVALS%") do ( set Y0=%%A set Y1=%%B set Y2=%%C ) if "%Y0%"=="." set Y0= if "%Y0%"=="" set Y0=0 if "%Y2%"=="." set Y2= if "%Y2%"=="" set Y2=1 if "%Y1%"=="." set Y1= if "%Y1%"=="" set Y1=((%Y0%)+(%Y2%))/2.0 set FMT=^ Y0=%%[fx:%Y0%]\n^ Y1=%%[fx:%Y1%]\n^ Y2=%%[fx:%Y2%] for /F "usebackq" %%L in (`%IMG7%magick identify ^ -precision 16 ^ -format "%FMT%" ^ xc:`) do set %%L echo %0: X1 = %X1% echo %0: Y0 = %Y0%, Y1 = %Y1%, Y2 = %Y2% set b=%Y0% set FMT=^ a=%%[fx:%Y2%-(%Y0%)]\n^ p=%%[fx:log((%Y1%-(%Y0%))/(%Y2%-(%Y0%)))/log(%X1%)] for /F "usebackq" %%L in (`%IMG7%magick identify ^ -precision 16 ^ -format "%FMT%" ^ xc:`) do set %%L echo %0: a = %a%, b = %b%, p = %p% set FMT=^ xAtZero=%%[fx:pow(-(%b%)/(%a%),1/(%p%))]\n^ xAtOne=%%[fx:pow((1-(%b%))/(%a%),1/(%p%))]\n for /F "usebackq" %%L in (`%IMG7%magick identify ^ -precision 16 ^ -format "%FMT%" ^ xc:`) do set %%L echo %0: xAtZero = %xAtZero% echo %0: xAtOne = %xAtOne% %IMG7%magick ^ %INFILE% ^ -evaluate Pow %p% ^ -function Polynomial %a%,%b% ^ %OUTFILE% if not "%DEBUG%"=="" ( %IMG7%magick ^ -size 1x256 gradient: -rotate 90 ^ -evaluate Pow %p% ^ -function Polynomial %a%,%b% ^ %DEBUG% call %PICTBAT%graphLineCol %DEBUG% . . 0 %DEBUG% ) call echoRestore @endlocal & set psmhOUTFILE=%OUTFILE%
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)
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 powsmh.h1. To re-create this web page, execute "procH1 powsmh".
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 9-November-2017.
Page created 25-Aug-2022 23:58:29.
Copyright © 2022 Alan Gibson.