snibgo's ImageMagick pages

Maximise local colour contrast

... without clipping.

The page Maximise local contrast shows how we can maximise local tonal contrast, varying what we mean by "local", and using a blend to control the degree of the effect.

This page applies the same method to the L*a*b* channels of an image to increase or maximise local colour contrast. This increases saturation, but also increases the local differences between colours.

The method

The method described in Maximise local contrast is applied independently to all three channels: L*, a* and b*. There is an option to affect only L*, or only a* and b*, in which case the other channels are replaced by those from the input.

Channels a* and b* have their neutral point at 50%, so multiply and divide must be with respect to that point. We subtract 50% from those channels, so for %INLAB%%BLR% and %MAXC% they nominally range from -50% to +50%. However, %MINBLR% varies from -100% to +100 in all channels, so %DIV% varies from 0 to 100%.

The script

The script maxLocCol.bat does the work.

Option Description
%1 Input image.
%2 Output file.
%3 Sigma to find the local mean. Default: 2.
%4 Sigma to find local maxima. Default: %3.
%5 Blend percentage. Default: 100.
%6 Apply to channels Lab or L or ab. Default: Lab.
%7 Post-process, eg -auto-level. Default: no post-process.

For example:

set SRC=toes.png

call %PICTBAT%maxLocCol ^
  %SRC% ^
  mlcl_sc1.png 0.5
mlcl_sc1.pngjpg
call %PICTBAT%maxLocCol ^
  %SRC% ^
  mlcl_sc2.png 1
mlcl_sc2.pngjpg
call %PICTBAT%maxLocCol ^
  %SRC% ^
  mlcl_sc3.png 2
mlcl_sc3.pngjpg
call %PICTBAT%maxLocCol ^
  %SRC% ^
  mlcl_sc4.png 3
mlcl_sc4.pngjpg
call %PICTBAT%maxLocCol ^
  %SRC% ^
  mlcl_sc5.png 10
mlcl_sc5.pngjpg
call %PICTBAT%maxLocCol ^
  %SRC% ^
  mlcl_sc6.png 20
mlcl_sc6.pngjpg
call %PICTBAT%maxLocCol ^
  %SRC% ^
  mlcl_sc7.png 50
mlcl_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%maxLocCol ^
  %SRC% ^
  mlcl_sc13.png 1 3
mlcl_sc13.pngjpg
call %PICTBAT%maxLocCol ^
  %SRC% ^
  mlcl_sc31.png 3 1
mlcl_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%maxLocCol ^
  %SRC% ^
  mlcl_bz0.png 0.5 . 25
mlcl_bz0.pngjpg
call %PICTBAT%maxLocCol ^
  %SRC% ^
  mlcl_bz1.png 2 . 25
mlcl_bz1.pngjpg
call %PICTBAT%maxLocCol ^
  %SRC% ^
  mlcl_b1.png 0.5 . 50
mlcl_b1.pngjpg
call %PICTBAT%maxLocCol ^
  %SRC% ^
  mlcl_b2.png 2 . 50
mlcl_b2.pngjpg
call %PICTBAT%maxLocCol ^
  %SRC% ^
  mlcl_b3.png 2 . 150
mlcl_b3.pngjpg
call %PICTBAT%maxLocCol ^
  %SRC% ^
  mlcl_b4.png 2 . -150
mlcl_b4.pngjpg

By default, the process modifies all three channel of Lab. Instead, the operation can be restricted to just L or ab.

All three channels:

call %PICTBAT%maxLocCol ^
  %SRC% ^
  mlcl_lab.png 0.5 . 50 Lab
mlcl_lab.pngjpg

Just the L* channel:

call %PICTBAT%maxLocCol ^
  %SRC% ^
  mlcl_l.png 0.5 . 50 L
mlcl_l.pngjpg

Just the a* and b* channels:

call %PICTBAT%maxLocCol ^
  %SRC% ^
  mlcl_ab.png 0.5 . 50 ab
mlcl_ab.pngjpg

Restricting to just the L* channel gives a similar result to Maximise local contrast, but is slower and uses more memory.

Restrict to just a* and b*, at two different sigmas, and three different blend percentages.

call %PICTBAT%maxLocCol ^
  %SRC% ^
  mlcl_m1.png 0.5 . 50 ab

call %PICTBAT%maxLocCol ^
  %SRC% ^
  mlcl_m1b.png 2 . 50 ab
mlcl_m1.pngjpg mlcl_m1b.pngjpg
call %PICTBAT%maxLocCol ^
  %SRC% ^
  mlcl_m2.png 0.5 . 100 ab

call %PICTBAT%maxLocCol ^
  %SRC% ^
  mlcl_m2b.png 2 . 100 ab
mlcl_m2.pngjpg mlcl_m2b.pngjpg
call %PICTBAT%maxLocCol ^
  %SRC% ^
  mlcl_m3.png 0.5 . 150 ab

call %PICTBAT%maxLocCol ^
  %SRC% ^
  mlcl_m3b.png 2 . 150 ab
mlcl_m3.pngjpg mlcl_m3b.pngjpg

The script also returns the name of a temporary file in mlclAVG. This image is black where there is no detail and lightest where there is most detail, so we can use it as a mask for the effect. (See also Adaptive blur and sharpen.) Working with the last example above:

Greatest effect in areas with detail

%IMG7%magick ^
  %SRC% ^
  mlcl_m3b.png ^
  ( %mlclAVG% -auto-level ) ^
  -compose Over -composite ^
  mlcl_mskd.png
mlcl_mskd.pngjpg

Greatest effect in areas with no detail

%IMG7%magick ^
  %SRC% ^
  mlcl_m3b.png ^
  ( %mlclAVG% -auto-level -negate ) ^
  -compose Over -composite ^
  mlcl_mskdn.png
mlcl_mskdn.pngjpg

JPEG source

For the source, we can use a JPEG image. However, the detail that is revealed may be JPEG artefacts:

Make a JPEG image

%IMG7%magick ^
  %SRC% ^
  -quality 50 ^
  mlcl_src_jpg.jpg
mlcl_src_jpg.jpg

Maximise local colour contrast

call %PICTBAT%maxLocCol ^
  mlcl_src_jpg.jpg ^
  mlcl_sj.png 0.5 . 100 ab
mlcl_sj.pngjpg

Increasing grizzle

The script makes grizzled old men even more so.

set SKIN_SRC=%PICTLIB%20130201\AGA_1201_sRGB.tiff

for /F "usebackq" %%L in (`%IMG7%magick identify ^
  -format "ss_size=%%wx%%h" ^
  %SKIN_SRC%`) do set %%L

echo ss_size = %ss_size% 
ss_size = 4924x7378 

As usual, we operate on the full-size image and create small versions for the web.

set WEBSIZE=-resize 600x600
%IMG7%magick ^
  %SKIN_SRC% ^
  -strip ^
  +write mlcl_face.miff ^
  %WEBSIZE% ^
  mlcl_face_sm.miff
mlcl_face_sm.miffjpg

Process just the L channel.

call %PICTBAT%maxLocCol ^
  mlcl_face.miff ^
  mlcl_face_mlc_l.miff 20 . . L

%IMG7%magick ^
  mlcl_face_mlc_l.miff ^
  %WEBSIZE% ^
  mlcl_face_mlcl_l_sm.miff
mlcl_face_mlcl_l_sm.miffjpg

Process just the ab channels.

call %PICTBAT%maxLocCol ^
  mlcl_face.miff ^
  mlcl_face_mlc_ab.miff 20 . . ab

%IMG7%magick ^
  mlcl_face_mlc_ab.miff ^
  %WEBSIZE% ^
  mlcl_face_mlcl_ab_sm.miff

The effect is subtle at small scale:
skin highlights and gray beard have become blue
(the complement of the skin colour).

mlcl_face_mlcl_ab_sm.miffjpg

Process all Lab channels.

call %PICTBAT%maxLocCol ^
  mlcl_face.miff ^
  mlcl_face_mlc.miff 20

%IMG7%magick ^
  mlcl_face_mlc.miff ^
  %WEBSIZE% ^
  mlcl_face_mlcl_sm.miff
mlcl_face_mlcl_sm.miffjpg

Show a 1:1 crop of all Lab channels:

%IMG7%magick ^
  mlcl_face_mlc.miff ^
  -crop 600x400+2800+2830 +repage ^
  mlcl_eye_mlc.jpg
mlcl_eye_mlc.jpg

As before, we can use %mlclAVG% as a mask to modulate the effect. We equalize the mask to get an even spread of the effect.

%IMG7%magick ^
  mlcl_face.miff ^
  mlcl_face_mlc.miff ^
  ( %mlclAVG% -equalize ) ^
  -compose Over -composite ^
  %WEBSIZE% ^
  mlcl_mod1.jpg
mlcl_mod1.jpg

From %SKIN_SRC%, an unpublished tool finds the bounding boxes for the eyes. This gives:

set leftEye=792x419+262+3157
set rightEye=1148x486+2585+2901

The script selCoordsBB.bat makes a mask, using the bounding boxes as gradient ellipses.

call %PICTBAT%selCoordsBB ^
  mlcl_eyes_msk.miff ^
  %ss_size% ^
  "%leftEye% %rightEye%"

%IMG7%magick ^
  mlcl_eyes_msk.miff ^
  %WEBSIZE% ^
  mlcl_eyes_msk_sm.miff
mlcl_eyes_msk_sm.miffjpg

We can then use this mask to apply the effect to just the eyes:

%IMG7%magick ^
  mlcl_face.miff ^
  mlcl_face_mlc.miff ^
  mlcl_eyes_msk.miff ^
  -compose Over -composite ^
  +write mlcl_eyes_comp_sm.miff ^
  %WEBSIZE% ^
  mlcl_eyes_comp_sm.miff
mlcl_eyes_comp_sm.miffjpg

Scripts

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

maxLocCol.bat

rem From image %1,
rem writes %2 with maximised local colour contrast
rem   (variation in L, a* and b*).
rem %3 sigma to find the local mean.
rem %4 sigma to find local maxima.
rem %5 blend percentage
rem %6 Lab or L or ab [default Lab]
rem      whether to maximize all L*a*b* channels,
rem      or just L*,
rem      or just a* and b*.
rem %7 Post-process, eg -auto-level
@rem
@rem Assumes magick is HDRI.
@rem
@rem Reference:
@rem   http://im.snibgo.com/maxloccol.htm

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

@setlocal enabledelayedexpansion

@call echoOffSave

call %PICTBAT%setInOut %1 mlc


set INFILE=%~1

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

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 CHANS=%6
if "%CHANS%"=="." set CHANS=
if "%CHANS%"=="" set CHANS=Lab

set POST_PROC=%7
if "%POST_PROC%"=="." set POST_PROC=

set BASE=%~n1

echo BASE=[%BASE%]

set TMPDIR=\temp\
set GRAY=%TMPDIR%mlcl_%BASE%_gray.miff
set INLAB=%TMPDIR%mlcl_%BASE%_inlab.miff
set BLR=%TMPDIR%mlcl_%BASE%_blr.miff
set MINBLR=%TMPDIR%mlcl_%BASE%_minblr.miff
set AVG=%TMPDIR%mlcl_%BASE%_minblr_avg.miff
set DIV=%TMPDIR%mlcl_%BASE%_minblr_div.miff
set MAXC=%TMPDIR%mlcl_%BASE%_maxc.miff


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

%IMG7%magick ^
  %INFILE% ^
  %sHDRI% ^
  -colorspace Lab ^
  -set colorspace sRGB ^
  -channel GB -evaluate Subtract 50%% +channel ^
  +write %INLAB% ^
  ( +clone ^
    -blur 0x%SIG1% ^
    +write %BLR% ^
  ) ^
  -compose MinusSrc -composite ^
  %MINBLR%

for /F "usebackq" %%L in (`%IMG7%magick ^
  %MINBLR% ^
  %sHDRI% ^
  -precision 19 ^
  -evaluate Abs 0 ^
  -format "maxAr=%%[fx:maxima.r]\nmaxAg=%%[fx:maxima.g]\nmaxAb=%%[fx:maxima.b]\n" ^
  +write info: ^
  -blur 0x%SIG2% ^
  +write %AVG% ^
  -format "maxBr=%%[fx:maxima.r]\nmaxBg=%%[fx:maxima.g]\nmaxBb=%%[fx:maxima.b]\n" ^
  info:`) do set %%L

for /F "usebackq" %%L in (`%IMG7%magick identify ^
  -precision 19 ^
  -format "multr=%%[fx:%maxAr%/%maxBr%]\nmultg=%%[fx:%maxAg%/%maxBg%]\nmultb=%%[fx:%maxAb%/%maxBb%]\n"
  xc:`) do set %%L

echo maxAr=%maxAr% maxBr=%maxBr% multr=%multr%
echo maxAg=%maxAg% maxBg=%maxBg% multg=%multg%
echo maxAb=%maxAb% maxBb=%maxBb% multb=%multb%

%IM7DEV%magick ^
  %MINBLR% ^
  %sHDRI% ^
  ( %AVG% ^
    -channel R -evaluate Multiply %multr% ^
    -channel G -evaluate Multiply %multg% ^
    -channel B -evaluate Multiply %multb% ^
    +channel ^
  ) ^
  -compose DivideSrc -composite ^
  -evaluate Divide 2 ^
  -evaluate Add 50%% ^
  -process 'oogbox' ^
  %DIV%

rem Next can't oogbox because a* and b* are -50% to +50%.

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


if /I %CHANS%==Lab (
  echo %0: channels Lab

  set READ_FILES=^
    %INLAB% ^
    %MAXC%

) else if /I %CHANS%==L (
  echo %0: channel L only

  set READ_FILES=^
    %INLAB% ^
    ^( +clone ^
       -channel GB -separate +channel ^
       ^( %MAXC% -channel R -separate +channel ^) ^
       -insert 0 ^
       -combine ^
    ^)

) else if /I %CHANS%==ab (
  echo %0: channels ab only

  set READ_FILES=^
    %INLAB% ^
    ^( +clone ^
       -channel R -separate +channel ^
       ^( %MAXC% -channel GB -separate +channel ^) ^
       -combine ^
    ^)

) else (
  echo %0: Bad CHANS [%CHANS%]
  exit /B 1
)

echo READ_FILES:
echo %READ_FILES%


%IM7DEV%magick ^
  %READ_FILES% ^
  %sHDRI% ^
  -compose blend -define compose:args=%BLENDPC% -composite ^
  -set colorspace Lab ^
  -channel GB -evaluate Add 50%% +channel ^
  -process 'oogbox' ^
  -colorspace sRGB ^
  -process 'oogbox' ^
  %POST_PROC% ^
  %OUTFILE%


call echoRestore

@endlocal & set mlclOUTFILE=%OUTFILE%& set mlclAVG=%AVG%

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)

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 maxloccol.h1. To re-create this web page, execute "procH1 maxloccol".


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 25-April-2018.

Page created 09-Aug-2022 05:06:03.

Copyright © 2022 Alan Gibson.