﻿

# Power curve for shadow, mid and highlights

With four numbers, we can tweak the bottom, top and somewhere in the middle.

## The script

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```  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```  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```  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 = -1.#IND F:\pictures\powSmh: xAtOne = 1.086799011023172```  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```  We can have negative transformations, where y0 > y1 > y2: 0.0 => 1.1, 0.5 => 0.4, 1.0 => -0.1. ```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.7776075786635522 F:\pictures\powSmh: xAtZero = 0.8941370136942889 F:\pictures\powSmh: xAtOne = 0.04094280203134165```  ## How does it work?

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.

• X0 always 0.0.
• X1 a value strictly between X0 and X2, default 0.5.
• X2 always 1.0.
• Y0 default 0.0.
• Y1 default (Y0+Y2)/2.
• Y2 default 1.0.

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).

## Lift, gamma, gain

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.

## Scripts

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

### powSmh.bat

```@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.

@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 (`%IM%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 (`%IM%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 (`%IM%identify ^
-precision 16 ^
-format "%FMT%" ^
xc:`) do set %%L

echo %0: xAtZero = %xAtZero%
echo %0: xAtOne = %xAtOne%

%IM%convert ^
%INFILE% ^
-evaluate Pow %p% ^
-function Polynomial %a%,%b% ^
%OUTFILE%

if not "%DEBUG%"=="" (
%IM%convert ^
-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:

`%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
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 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 12-Mar-2018 04:37:50.