snibgo's ImageMagick pages

Cluts and clipping

Cluts can cause clipping. We can adjust the clut to reduce or remove this problem.

A clut (colour look-up table) transforms an image, shifting pixel values. Each input value X% is transformed to a certain output value Y%. It generally increases contrast for some ranges of values, and decreases contrast in other ranges, while leaving the minimum and maximum values (0 and 100%) untouched.

A clut may push many pixels to exactly 0 or 100%. These pixels are no longer different. This may be bad news for aesthetic reasons, as well as inhibiting further image processing.

The problem often arises when a clut is derived from tonal values (intensity), but applied to each of the three colour channels. A certain pixel would be shifted by a certain amount according to its intensity, but instead each colour channel is shifted by a different amount.

Clipping can occur at other values, where a disproprtionate numer of pixels are given the same value. We must avoid solutions that merely raise the floor (or lower the ceiling) at which clipping occurs.

Computer LCD monitors are often poor at distinguishing between tones that are very dark or very light, so we may wish to reduce any darkening of dark tones, and lightening of light tones, even when there is no clipping.

We might also wish to de-clip an image: where clipping has already occured, shift the values of those pixels. That process is not considered on this page. (It is better to prevent the problem in the first place.)

Scripts on this page use process modules. For details of process modules and building with IM, see my page Customising ImageMagick.

Is an image clipped? Will this clut applied to this image cause clipping? Reducing shift into shadows and hightlights.

Create sample files

Scripts

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

histClipped.bat

rem From non-cumulised histogram %1,
rem find clipping.
@rem
@rem Returns hcCLIP_LO, hcCLIP_HI.
@rem Compares first/last value to window of values.
@rem If this=0 or this < max(window), returns 0
@rem Else if mean(window)=0, returns 99999
@rem Else returns this/mean(window)
@rem
@rem Can also use:
@rem   hcWINDOW_PC window width, as percent of histogram width. Default 10.
@rem
@rem Examines ONLY first and last buckets.
@rem Histogram might be pre-processed, removing empty buckets at start and end.

@rem Also problem if either window has MAX=1, or much above average of entire histogram.


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

@setlocal enabledelayedexpansion

rem @call echoOffSave

call %PICTBAT%setInOut %1 hc

if "%hcWINDOW_PC%"=="" set hcWINDOW_PC=10

for /F "usebackq" %%L in (`%IM%convert ^
  %INFILE% ^
  -format "MEAN_ALL=%%[fx:mean]\nMAX_ALL=%%[fx:maxima]\n" ^
  -write info: ^
  ^( +clone ^
    -crop %hcWINDOW_PC%%%x1+0+0 +repage ^
    -format "END_LO=%%[fx:p{0,0}.intensity]\n" ^
    -write info: ^
    -crop +1+0 +repage ^
    -format "MEAN_LO=%%[fx:mean]\nMAX_LO=%%[fx:maxima]\n" ^
    -write info: ^
    +delete ^) ^
  ^( +clone -flop ^
    -crop %hcWINDOW_PC%%%x1+0+0 +repage ^
    -format "END_HI=%%[fx:p{0,0}.intensity]\n" ^
    -write info: ^
    -crop +1+0 +repage ^
    -format "MEAN_HI=%%[fx:mean]\nMAX_HI=%%[fx:maxima]\n" ^
    -write info: ^
    +delete ^) ^
  NULL:`) do set %%L

if %END_LO%==0 (
  set CLIP_LO=0
) else if %MEAN_LO%==0 (
  set CLIP_LO=99999
) else (
  for /F "usebackq" %%L in (`%IM%identify ^
    -format "CLIP_LO=%%[fx:%END_LO%<=%MAX_LO%?0:%END_LO%/%MEAN_LO%]" ^
    xc:`) do set %%L
)

if %END_HI%==0 (
  set CLIP_HI=0
) else if %MEAN_HI%==0 (
  set CLIP_HI=99999
) else (
  for /F "usebackq" %%L in (`%IM%identify ^
    -format "CLIP_HI=%%[fx:%END_HI%<=%MAX_HI%?0:%END_HI%/%MEAN_HI%]" ^
    xc:`) do set %%L
)

if "%MAX_LO%"=="1" (
  set CLIP_LO_WIN=1
  for /F "usebackq" %%L in (`%IM%identify ^
    -format "CLIP_LO_WIN=%%[fx:%MAX_LO%/%MEAN_LO%]" ^
    xc:`) do set %%L
) else (
  set CLIP_LO_WIN=0
)

if "%MAX_HI%"=="1" (
  for /F "usebackq" %%L in (`%IM%identify ^
    -format "CLIP_HI_WIN=%%[fx:%MAX_HI%/%MEAN_HI%]" ^
    xc:`) do set %%L
) else (
  set CLIP_HI_WIN=0
)

echo CLIP_LO=%CLIP_LO%  CLIP_HI=%CLIP_HI% CLIP_LO_WIN=%CLIP_LO_WIN%  CLIP_HI_WIN=%CLIP_HI_WIN%

call echoRestore

@endlocal & set hcCLIP_LO=%CLIP_LO%& set hcCLIP_HI=%CLIP_HI%

histToeInt.bat

rem Finds intersection of histogram and %1 slopes at shadow and highlight.
rem %2 is slope, percentage of 45 degrees, 0 to 100.
rem returns X_LO and X_HI as percentages of the histogram width,
rem and Y_LO and Y_HI, as percentages of Qauntum.

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

@setlocal enabledelayedexpansion

rem @call echoOffSave

call %PICTBAT%setInOut %1 hti


set SLOPE_PC=%2
if "%SLOPE_PC%"=="" set SLOPE_PC=20

%IMDEV%convert ^
  %INFILE% ^
  ( -clone 0 ^
    ( +clone ^
      -sparse-color bilinear "0,0,#000,%%[fx:w-1],0,gray(%SLOPE_PC%%%)" ^
      -evaluate Add 0%% ^
      -write mpr:RAMP ^
    ) ^
    -compose MinusSrc -composite ^
    -clamp ^
    -fill #fff +opaque #000 ^
    -process onewhite ^
    +delete ^
  ) ^
  ( -clone 0 ^
    -flop ^
    mpr:RAMP ^
    -compose MinusSrc -composite ^
    -clamp ^
    -fill #fff +opaque #000 ^
    -process onewhite ^
    +delete ^
  ) ^
  NULL:


call echoRestore

@endlocal & set eqsOUTFILE=%OUTFILE%

equSlope.bat

rem From image %1, makes other image
rem using a cumulative histogram with specified slopes at shadow and highlight.
rem
rem %2 is percentage along x-axis for shadow slope. [0]
rem %3 is percentage along x-axis for start of highlight slope. [100]
rem %4 is percentage up y-axis for shadow slope. [same as %2]
rem %5 is percentage up y-axis for start of highlight slope. [same as %3]
rem %6 is optional output filename.


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

@setlocal enabledelayedexpansion

@call echoOffSave

call %PICTBAT%setInOut %1 eqs

if not "%6"=="" set OUTFILE=%6

set TMP_HIST=%INNAME%_%sioCODE%_hist.miff

%IMDEV%convert ^
  %INFILE% ^
  -colorspace Gray ^
  -process 'mkhisto norm' ^
  %TMP_HIST%

call %PICTBAT%equSlopeH %TMP_HIST% %2 %3 %4 %5

%IMDEV%convert ^
  %INFILE% ^
  %eqshOUTFILE% ^
  -clut ^
  %OUTFILE%

goto end


set AX=%2
set BX=%3
set AY=%4
set BY=%5

if "%AX%"=="." set AX=
if "%BX%"=="." set BX=
if "%AY%"=="." set AY=
if "%BY%"=="." set BY=

if "%AX%"=="" set AX=0
if "%BX%"=="" set BX=100
if "%AY%"=="" set AY=%AX%
if "%BY%"=="" set BY=%BX%

echo %AX% %BX% %AY% %BY%

if not "%6"=="" set OUTFILE=%6

for /F "usebackq" %%L in (`%IM%identify -ping ^
  -format "Wm1=%%[fx:w-1]\nAXP=%%[fx:int(w*%AX%/100+0.5)]\nBXP=%%[fx:int(w*%BX%/100+0.5)]\nAXP2=%%[fx:w-int(w*%AX%/100+0.5)]\nBXP2=%%[fx:w-int(w*%BX%/100+0.5)]\n" ^
  %INFILE%`) do set %%L

echo AXP=%AXP% BXP=%BXP% AXP2=%AXP2% BXP2=%BXP2%

if %AX%==0 (
  set BKL=
  set WHR=
) else (
  set BKL=-size %AXP%x1 xc:#000 -gravity West -composite
  set WHR=-size %AXP2%x1 xc:#fff -gravity East -composite

  set G1=  ^( -clone 0 ^
    -sparse-color bilinear 0,0,#000,%Wm1%,0,#fff ^
    !WHR! ^
    +write G1.png ^
  ^) ^
  -evaluate-sequence Min

)

if %BX%==100 (
  set BKR=
  set WHL=
) else (
  set BKR=-size %BXP2%x1 xc:#000 -gravity East -composite
  set WHL=-size %BXP%x1 xc:#000 -gravity West -composite

  set G2=  ^( -clone 0 ^
    -sparse-color bilinear 0,0,#000,%Wm1%,0,#fff ^
    !WHL! ^
    +write G2.png ^
  ^) ^
  -evaluate-sequence Max

)

%IMDEV%convert ^
  %INFILE% ^
  %BKL% ^
  %BKR% ^
  -process 'cumulhisto norm' ^
  +level %AY%%%,%BY%%% ^
  %G1% ^
  %G2% ^
  %OUTFILE%

call %PICTBAT%graphLineCol %OUTFILE%


:end

call echoRestore

@endlocal & set eqsOUTFILE=%OUTFILE%

equSlopeH.bat

rem From %1 a histogram,
rem makes a cumulative histogram but with specified slopes at shadow and highlight.
rem
rem %2 is percentage along x-axis for shadow slope. [0]
rem %3 is percentage along x-axis for start of highlight slope. [100]
rem %4 is percentage up y-axis for shadow slope. [same as %2]
rem %5 is percentage up y-axis for start of highlight slope. [same as %3]
rem %6 is optional output filename.


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

@setlocal enabledelayedexpansion

@call echoOffSave

call %PICTBAT%setInOut %1 eqsh

set AX=%2
set BX=%3
set AY=%4
set BY=%5

if "%AX%"=="." set AX=
if "%BX%"=="." set BX=
if "%AY%"=="." set AY=
if "%BY%"=="." set BY=

if "%AX%"=="" set AX=0
if "%BX%"=="" set BX=100
if "%AY%"=="" set AY=%AX%
if "%BY%"=="" set BY=%BX%

rem echo %AX% %BX% %AY% %BY%

if not "%6"=="" set OUTFILE=%6

for /F "usebackq" %%L in (`%IM%identify -ping ^
  -format "Wm1=%%[fx:w-1]\nAXP=%%[fx:int(w*%AX%/100+0.5)]\nBXP=%%[fx:int(w*%BX%/100+0.5)]\nAXP2=%%[fx:w-int(w*%AX%/100+0.5)]\nBXP2=%%[fx:w-int(w*%BX%/100+0.5)]\n" ^
  %INFILE%`) do set %%L

rem echo AXP=%AXP% BXP=%BXP% AXP2=%AXP2% BXP2=%BXP2%

if "%eqshDEBUG%"=="1" (
  set WR_G1=+write %~n1_G1.png
  set WR_G2=+write %~n1_G2.png
) else (
  set WR_G1=
  set WR_G2=
)

if %AX%==0 (
  set BKL=
  set G1=
) else (
  set BKL=-size %AXP%x1 xc:#000 -gravity West -composite

  set G1=^( -size %AXP%x1 gradient:black-gray^(%AY%%%^) ^
    -size %AXP2%x1 xc:#fff  ^
    +append ^
    %WR_G1% ^
  ^) ^
  -evaluate-sequence Min

)

if %BX%==100 (
  set BKR=
  set G2=
) else (
  set BKR=-size %BXP2%x1 xc:#000 -gravity East -composite

  set G2=^( -size %BXP%x1 xc:#000 ^
    -size %BXP2%x1 gradient:gray^(%BY%%%^)-white ^
    +append ^
    %WR_G2% ^
  ^) ^
  -evaluate-sequence Max

)

%IMDEV%convert ^
  %INFILE% ^
  %BKL% ^
  %BKR% ^
  -process 'cumulhisto norm' ^
  +level %AY%%%,%BY%%% ^
  %G1% ^
  %G2% ^
  %OUTFILE%

if "%eqshDEBUG%"=="1" (
  call %PICTBAT%graphLineCol %OUTFILE%
  call %PICTBAT%graphLineCol %~n1_G1.png
  call %PICTBAT%graphLineCol %~n1_G2.png
)

call echoRestore

@endlocal & set eqshOUTFILE=%OUTFILE%

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

%IM%identify -version
Version: ImageMagick 6.9.0-0 Q16 x64 2014-11-14 http://www.imagemagick.org
Copyright: Copyright (C) 1999-2014 ImageMagick Studio LLC
Features: DPC OpenMP
Delegates (built-in): bzlib cairo freetype jbig jng jp2 jpeg lcms lqr pangocairo png ps rsvg tiff webp xml zlib

Source file for this web page is clclip.h1. svg tiff webp xml zlib

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


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 30-Dec-2014.

Page created 24-Jun-2015 14:46:14.

Copyright © 2015 Alan Gibson.