snibgo's ImageMagick pages

Maximise local contrast

... without clipping.

In IM, we can increase global contrast with -level or a number of other operations, and we can increase local contrast with -unsharp. If we increase contrast by too much, we push values out of gamut, so they are clipped and we lose detail. How can we increase local contrast so it is maximised, without clipping?

The method

We will work in Q32 HDRI:

if "%IM32f%"=="" call %PICTBAT%setim8

set sHDRI=^
  -depth 32 ^
  -define "compose:clamp=off" ^
  -define "quantum:format=floating-point"

We start with an auto-levelled gray image, from the L channel of CIELab:

set SRC=toes.png
set SRC_GR=mlc_srcgr.miff

%IM32f%convert ^
  %SRC% ^
  %sHDRI% ^
  -colorspace Lab ^
  -channel R -separate +channel ^
  -set colorspace sRGB ^
  -auto-level ^
  %SRC_GR%
mlc_srcgr.miffjpg

Subtract the local mean. Values are close to zero, so we need floating-point to avoid clipping. The process will expand the range of these values.

set SIG1=2

%IM32f%convert ^
  %SRC_GR% ^
  %sHDRI% ^
  ( +clone ^
    -blur 0x%SIG1% ^
    +write mlc_blr.miff ^
  ) ^
  -compose MinusSrc -composite ^
  mlc_minblr.miff
mlc_blr.miffjpg mlc_minblr.miffjpg

mlc_minblr.miff contains high-frequency data (positive and negative). If we add it back to the blur (low-frequency data, positive only), this will reconstruct the original source image.

mlc_minblr.miff isn't easy to see, so we "-auto-level" to put pixels in the 0 to 100% range, just for viewing:

%IM32f%convert ^
  mlc_minblr.miff ^
  %sHDRI% ^
  -auto-level ^
  mlc_minblr_p.png
mlc_minblr_p.pngjpg

If we double the values in mlc_minblr.miff before adding it back to the blur, this will increase the local contrast:

%IM32f%convert ^
  mlc_minblr.miff ^
  %sHDRI% ^
  -evaluate Multiply 2 ^
  mlc_blr.miff ^
  -compose Plus -composite ^
  mlc_uns1.miff
mlc_uns1.miffjpg

This operation will usually cause some clipping, as it has done here:

%IM32f%convert ^
  mlc_uns1.miff ^
  -format "min=%%[fx:minima]  max=%%[fx:maxima]\n" ^
  info: 
min=-0.0840412  max=1.12593

From the image we see that most areas have not clipped. In those areas, we could multiply mlc_minblr.miff by a larger number before adding it back to the blur. To maximise local contrast everywhere without clipping, we will multiply by a number that varies across the image. This is most conveniently done by dividing by the reciprocal, something like this ...

%IM32f%convert ^
  mlc_minblr.miff ^
  %sHDRI% ^
  mlc_numbers.miff ^
  -compose Divide -composite ^
  mlc_blr.miff ^
  -compose Plus -composite ^
  mlc_max_cont.miff

... where mlc_numbers.miff contains the divider at each pixel. So now we need to calculate mlc_numbers.miff. Ideally, this would be the local peaks in mlc_minblr.miff, but this wouldn't be a smoothly changing function. mlc_minblr.miff contains positive and negative values, so we take the absolute value, and blur it. This smooths the peaks, so we multiply it by the maximum absolute value divided by the maximum blurred absolute value. (It would be easier to -auto-level the blurred absolute, but outliers would squish the values too much.)

First, take the absolute and blur it, and calculate the multiplier...

set SIG2=%SIG1%

for /F "usebackq" %%L in (`%IM32f%convert ^
  mlc_minblr.miff ^
  %sHDRI% ^
   -evaluate Abs 0 ^
   +write mlc_minblr_abs.miff ^
  ^( +clone ^
     -format "maxA=%%[fx:maxima]\n" ^
     +write info: ^
     +delete ^
  ^) ^
  -blur 0x%SIG2% ^
  +write mlc_minblr_avg.miff ^
  -format "maxB=%%[fx:maxima]\n" ^
  info:`) do set %%L

for /F "usebackq" %%L in (`%IM32f%identify ^
  -format "mult=%%[fx:%maxA%/%maxB%]\n"
  xc:`) do set %%L

echo maxA=%maxA% maxB=%maxB% mult=%mult% 
maxA=0.241885 maxB=0.081344 mult=2.97361 
mlc_minblr_abs.miffjpg mlc_minblr_avg.miffjpg

... and apply the multiplier:

%IM32f%convert ^
  mlc_minblr_avg.miff ^
  %sHDRI% ^
  -evaluate Multiply %mult% ^
  mlc_minblr_avg2.miff
mlc_minblr_avg2.miffjpg

Now we can divide mlc_minblr.miff by the numbers in mlc_minblr_avg2.miff to locally maximise the high frequency component.

%IM32f%convert ^
  mlc_minblr.miff ^
  %sHDRI% ^
  mlc_minblr_avg2.miff ^
  -compose DivideSrc -composite ^
  mlc_minblr_div.miff
mlc_minblr_div.miffjpg

mlc_minblr_div.miff ranges from -100% to +100%, more or less. -auto-level would reduce this to exactly 0 to 100%, but outliers would squish this by too much, so we use oogPower.bat (see Putting OOG back in the box: Power modulation).

%IM32f%convert ^
  mlc_minblr_div.miff ^
  %sHDRI% ^
  -evaluate Divide 2 ^
  -evaluate Add 50%% ^
  mlc_minblr_div2.miff
mlc_minblr_div2.miffjpg
call %PICTBAT%oogPower ^
  mlc_minblr_div2.miff ^
  mlc_minblr_div3.miff
mlc_minblr_div3.miffjpg

This is the high frequency component of the maximum-contrast image. For some purposes, this is what we want. However, it is more correct to add it back to the blur.

%IM32f%convert ^
  mlc_blr.miff ^
  %sHDRI% ^
  mlc_minblr_div3.miff ^
  -compose Mathematics ^
    -define compose:args=0,1.0,1.0,-0.5 ^
  -composite ^
  mlc_maxc.miff
mlc_maxc.miffjpg

This addition can cause clipping, so we do another oogPower:

call %PICTBAT%oogPower ^
  mlc_maxc.miff ^
  mlc_maxc2.miff
mlc_maxc2.miffjpg

Restore the colour:

%IM32f%convert ^
  %SRC% ^
  %sHDRI% ^
  -colorspace Lab ^
  -separate ^
  mlc_maxc2.miff ^
  -swap 0,3 ^
  +delete ^
  -combine ^
  -set colorspace Lab ^
  -colorspace sRGB ^
  mlc_maxc3.miff
mlc_maxc3.miffjpg

The script

We put the above into a script, maxLocCont.bat.

call %PICTBAT%maxLocCont ^
  %SRC% ^
  mlc_sc1.png 0.5
mlc_sc1.pngjpg
call %PICTBAT%maxLocCont ^
  %SRC% ^
  mlc_sc2.png 1
mlc_sc2.pngjpg
call %PICTBAT%maxLocCont ^
  %SRC% ^
  mlc_sc3.png 2
mlc_sc3.pngjpg
call %PICTBAT%maxLocCont ^
  %SRC% ^
  mlc_sc4.png 3
mlc_sc4.pngjpg
call %PICTBAT%maxLocCont ^
  %SRC% ^
  mlc_sc5.png 10
mlc_sc5.pngjpg
call %PICTBAT%maxLocCont ^
  %SRC% ^
  mlc_sc6.png 20
mlc_sc6.pngjpg
call %PICTBAT%maxLocCont ^
  %SRC% ^
  mlc_sc7.png 50
mlc_sc7.pngjpg

Generally, the two sigmas should be equal. If the second is larger than the first, the contrast is reduced. If the second is smaller than the first, contrast is increased.

call %PICTBAT%maxLocCont ^
  %SRC% ^
  mlc_sc13.png 1 3
mlc_sc13.pngjpg
call %PICTBAT%maxLocCont ^
  %SRC% ^
  mlc_sc31.png 3 1
mlc_sc31.pngjpg

We can use the blend parameter from 0 to 100 to control the degree of the effect, or use a value beyond that range.

call %PICTBAT%maxLocCont ^
  %SRC% ^
  mlc_b1.png 2 . 50
mlc_b1.pngjpg
call %PICTBAT%maxLocCont ^
  %SRC% ^
  mlc_b2.png 2 . 150
mlc_b2.pngjpg
call %PICTBAT%maxLocCont ^
  %SRC% ^
  mlc_b3.png 2 . -150
mlc_b3.pngjpg

Scripts

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

maxLocCont.bat

rem From image %1,
rem writes %2 with maximised local contrast.
rem %3 sigma
rem %4 sigma
rem %5 blend percentage

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

@setlocal enabledelayedexpansion

rem @call echoOffSave

rem call %PICTBAT%setInOut %1 mlc


set INFILE=%~1

set OUTFILE=%2
if "%OUTFILE%"=="." set OUTFILE=
if "%OUTFILE%"=="" set OUTFILE=NULL:

set SIG1=%3
if "%SIG1%"=="." set SIG1=
if "%SIG1%"=="" set SIG1=2

set SIG2=%4
if "%SIG2%"=="." set SIG2=
if "%SIG2%"=="" set SIG2=%SIG1%

set BLENDPC=%5
if "%BLENDPC%"=="." set BLENDPC=
if "%BLENDPC%"=="" set BLENDPC=100

set TMPDIR=\temp\
set GRAY=%TMPDIR%mlc_gray.miff
set BLR=%TMPDIR%mlc_blr.miff
set MINBLR=%TMPDIR%mlc_minblr.miff
set AVG=%TMPDIR%mlc_minblr_avg.miff
set DIV=%TMPDIR%mlc_minblr_div.miff
set MAXC=%TMPDIR%mlc_maxc.miff


if "%IM32f%"=="" call %PICTBAT%setim8

set sHDRI=^
  -depth 32 ^
  -define "compose:clamp=off" ^
  -define "quantum:format=floating-point"

if %BLENDPC%==100 (
  set WR_GRAY=
) else (
  set WR_GRAY=+write %GRAY%
)

%IM32f%convert ^
  %INFILE% ^
  %sHDRI% ^
  -colorspace Lab ^
  -channel R -separate +channel ^
  -set colorspace sRGB ^
  %WR_GRAY% ^
  -auto-level ^
  ( +clone ^
    -blur 0x%SIG1% ^
    +write %BLR% ^
  ) ^
  -compose MinusSrc -composite ^
  %MINBLR%

for /F "usebackq" %%L in (`%IM32f%convert ^
  %MINBLR% ^
  %sHDRI% ^
   -evaluate Abs 0 ^
  ^( +clone ^
     -format "maxA=%%[fx:maxima]\n" ^
     +write info: ^
     +delete ^
  ^) ^
  -blur 0x%SIG2% ^
  +write %AVG% ^
  -format "maxB=%%[fx:maxima]\n" ^
  info:`) do set %%L

for /F "usebackq" %%L in (`%IM32f%identify ^
  -format "mult=%%[fx:%maxA%/%maxB%]\n"
  xc:`) do set %%L

echo maxA=%maxA% maxB=%maxB% mult=%mult%

%IM32f%convert ^
  %MINBLR% ^
  %sHDRI% ^
  ( %AVG% ^
    -evaluate Multiply %mult% ^
  ) ^
  -compose DivideSrc -composite ^
  -evaluate Divide 2 ^
  -evaluate Add 50%% ^
  %DIV%

call %PICTBAT%oogPower ^
  %DIV% ^
  %DIV%

%IM32f%convert ^
  %BLR% ^
  %sHDRI% ^
  %DIV% ^
  -compose Mathematics ^
    -define compose:args=0,1.0,1.0,-0.5 ^
  -composite ^
  %MAXC%

call %PICTBAT%oogPower ^
  %MAXC% ^
  %MAXC%

if %BLENDPC%==100 (
  set CHAN_L=%MAXC%
) else (
  set CHAN_L=%GRAY% %MAXC% -compose blend -define compose:args=%BLENDPC% -composite
)

%IM32f%convert ^
  %INFILE% ^
  %sHDRI% ^
  -colorspace Lab ^
  -separate ^
  ( %CHAN_L% ) ^
  -swap 0,3 ^
  +delete ^
  -combine ^
  -set colorspace Lab ^
  -colorspace sRGB ^
  %OUTFILE%



call echoRestore

@endlocal & set mlcOUTFILE=%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
License: http://www.imagemagick.org/script/license.php
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
%IM32f%identify -version
Version: ImageMagick 6.9.0-0 Q32 x86_64 2015-02-28 http://www.imagemagick.org
Copyright: Copyright (C) 1999-2014 ImageMagick Studio LLC
Features: DPC HDRI Modules OpenMP
Delegates (built-in): bzlib fontconfig freetype fpx jbig jng jpeg lcms ltdl lzma png tiff x 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 maxloccont.h1. To re-create this web page, execute "procH1 maxloccont".


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 4-December-2017.

Page created 04-Dec-2017 07:56:30.

Copyright © 2017 Alan Gibson.