snibgo's ImageMagick pages

Adaptive blur and sharpen

Modulating the effect by the existing sharpness.

We sometimes want to blur areas that are already blurred, or sharpen areas that are already sharp, or both, or some other combination.

The Slopes page shows how we can do this by modulating the blur by the slope. This page develops that method.

The script on this page applies a Gaussian (circular) blur. It is isotropic, meaning the same effect is applied in all directions. A related effect applies a linear blur/sharpen, either parallel or perpendicular to contours, so it is anisotropic. See adapContBlrShp.

Sample input

We will demonstrate on the usual image:

set SRC=toes.png

The script

The script adapBlrShp.bat blurs or sharpens an image, with the greatest effect either where there is most detail (the edges) or where there is least detail (non-edges).

Parameter Description
%1 Input image (mandatory parameter)
%2 Output image
%3 One of:
  • E blur at the edges (where the mask is light)
  • N blur at the non-edges (where the mask is dark)
Default: N
%4 Edge detector sigma
Default: 10
%5 Blur sigma
Default: half of %4
%6 Blend percentage
  • 0 for no effect
  • >0 for blur
  • <0 for sharpen
Default: 100
%7 Colorspace for operation

The input is mandatory. All other parameters are optional. As usual, a dot "." may be used as a placeholder.

The script adapBlrShp.bat blurs an image using a mask that is white where there is most detail, and black where there is least detail. By default the mask is negated, so the blur is greatest where there is least detail. The blurred result is blended with the input at a certain percentage. The default percentage is 100 so the result is the blur. Alternative percentages include zero so there is no effect, and -100 so the effect is a sharpening. Other values can be used, eg 50 for a reduced blur or 150 for an increased blur.

Blur sigma is the primary control for the amount of blurring, with positive blend percentages between 0 and 100 aimed at animation control. (Blending blur with the original creates ghosting.) However, negative blend percentages are used for sharpening by unsharp-mask.

Blend interpolation (between 0 and 100, inclusive) is unlikely to cause OOG and hence clipping. But blend extrapolation (negative values) is likely to cause OOG and hence clipping. Indeed, if the input is auto-levelled, extrapolation is almost certain to cause clipping. See Preventing clipping below.

By default, the mask is made from the magnitude of the slopes of a grayscale version of the input, where the slopes are found by the comet blur method. However, a user-supplied mask can be used instead.

There are four basic modes of operation:

  1. blur non-edges
  2. blur edges
  3. sharpen non-edges
  4. sharpen edges

Blur non-edges (default)

call %PICTBAT%adapBlrShp ^
  %SRC% ^

Blur the edges

call %PICTBAT%adapBlrShp ^
  %SRC% ^
  abs_ex2.png E

Sharpen non-edges

call %PICTBAT%adapBlrShp ^
  %SRC% ^
  abs_ex3.png . . . -100

Sharpen the edges

call %PICTBAT%adapBlrShp ^
  %SRC% ^
  abs_ex4.png E . . -100

Over-sharpen the non-edges

call %PICTBAT%adapBlrShp ^
  %SRC% ^
  abs_ex5.png N . . -200

Over-sharpen the edges

call %PICTBAT%adapBlrShp ^
  %SRC% ^
  abs_ex6.png E . . -200


Instead of the default mask (and default processing of that mask), the user can supply a mask:

Make a mask

%IMG7%magick ^
  -size 267x233 ^
  radial-gradient: ^

Apply the mask

set absRD_MASK=abs_radmask.png

call %PICTBAT%adapBlrShp ^
  %SRC% ^

set absRD_MASK=

Apply the mask, blurring the "edges"
(where the mask is white)

set absRD_MASK=abs_radmask.png

call %PICTBAT%adapBlrShp ^
  %SRC% ^
  abs_rm2.png E

set absRD_MASK=


There are two sigma controls: one for the edge detector, and one for the actual blur.

The mask sigma can be large:

Blur non-edges

set absWR_MASK=abs_msk_s1.png

call %PICTBAT%adapBlrShp ^
  %SRC% ^
  abs_s1.png N 30

set absWR_MASK=
abs_s1.pngjpg abs_msk_s1.pngjpg

The mask from a comet blur is too sharp. We can blur before the stretch, and after:

Blur non-edges

set absWR_MASK=abs_msk_s2.png
set absMSK_PROC=^
-blur 0x5 -contrast-stretch 40%%x50%% -blur 0x5

call %PICTBAT%adapBlrShp ^
  %SRC% ^
  abs_s2.png N 30

set absMSK_PROC=
set absWR_MASK=
abs_s2.pngjpg abs_msk_s2.pngjpg


Any operation that mixes colours from a number of pixels, such as blur and sharpen, is more correctly done in linear colorspace. (Well, that should read scene-referred colorspace, but we assume linear is a good approximation.)

call %PICTBAT%abs_colsp %SRC%
Colorspace blur non-edges blur edges sharpen non-edges sharpen edges
sRGB abs_csnb_sRGB.pngjpg abs_cseb_sRGB.pngjpg abs_csns_sRGB.pngjpg abs_cses_sRGB.pngjpg
RGB abs_csnb_RGB.pngjpg abs_cseb_RGB.pngjpg abs_csns_RGB.pngjpg abs_cses_RGB.pngjpg
Lab abs_csnb_Lab.pngjpg abs_cseb_Lab.pngjpg abs_csns_Lab.pngjpg abs_cses_Lab.pngjpg

There is practically no difference between the colorspace versions.

Blur and sharpen

We can call the script to blur, and call it again to sharpen that result:

call %PICTBAT%adapBlrShp ^
  %SRC% ^

call %PICTBAT%adapBlrShp ^
  abs_bands.png ^
  abs_bands.png E . . -100

Or we can save the mask used in the blur, and use the same mask for sharpening.

set absWR_MASK=abs_msk.png

call %PICTBAT%adapBlrShp ^
  %SRC% ^

set absWR_MASK=

We show the intermediate result and the mask.

abs_bands2.pngjpg abs_msk.pngjpg
set absRD_MASK=abs_msk.png

call %PICTBAT%adapBlrShp ^
  abs_bands2.png ^
  abs_bands2s.png E . . -100

set absRD_MASK=

The default settings give a fairly sharp line between the blurred and sharpened areas. We can cure this by setting absMSK_PROC:

set absMSK_PROC=-contrast-stretch 40%%x40%% -blur 0x5
set absWR_MASK=abs_msk3.png

call %PICTBAT%adapBlrShp ^
  %SRC% ^

set absWR_MASK=
set absMSK_PROC=

We show the intermediate result and the mask.

abs_bands3.pngjpg abs_msk3.pngjpg
set absRD_MASK=abs_msk3.png

call %PICTBAT%adapBlrShp ^
  abs_bands3.png ^
  abs_bands3s.png E . . -100

set absRD_MASK=

Preventing clipping

Extrapolation can cause clipping. Speaking more correctly, it can cause values to go out of the range 0 to 100%, which is out of gamut (OOG), which will be clipped by most file formats.

call %PICTBAT%adapBlrShp ^
  %SRC% ^
  abs_shp.miff E . . -200

%IMG7%magick ^
  abs_shp.miff ^
  -format "min=%%[fx:minima]  max=%%[fx:maxima]\n" ^
min=0  max=1

The script oogPower.bat pushes values back into gamut. See Putting OOG back in the box. (This is also avilable as a process module.)

call %PICTBAT%oogPower ^
  abs_shp.miff abs_shp2.miff

%IMG7%magick ^
  abs_shp2.miff ^
  -format "min=%%[fx:minima]  max=%%[fx:maxima]\n" ^
min=0  max=1

Alternative: Poisson-smoothing

An alternative is to zero slopes that are less than a certain percentage, and reconstruct with Poisson-pasting. See Seamless photomontage: modulating texture.

call %PICTBAT%poissonSmooth ^
  %SRC% abs_psm.png 3

Make it prettier

%IM7DEV%magick ^
  abs_psm.png ^
  -auto-level ^
  -process 'setmnsd sd 0.16667 d inconly' ^
  -unsharp 0x1 ^


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


rem From image %1,
rem makes output %2 blurred most at edges, or most at non-edges.
rem %3 E=blur at the edges, or N=blur at non-edges.
rem %4 edge detector sigma
rem %5 blur sigma
rem %6 blend percentage: 0=no effect, 100=blur, -100=sharpen
rem %7 colorspace
@rem Also uses:
@rem   absWR_MASK if not blank, writes mask to this file
@rem   absRD_MASK if not blank, reads mask from this file
@rem   absMSK_PROC
@rem   absMSK_PRE
@rem Updated:
@rem   16-December-2017 Corrected PREV_COLSP bug.
@rem   29-August-2022 for IM v7.

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

@setlocal enabledelayedexpansion

rem @call echoOffSave

call %PICTBAT%setInOut %1 abs

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

set BLR_AT=%3
if "%BLR_AT%"=="." set BLR_AT=
if "%BLR_AT%"=="" set BLR_AT=N

set EDGE_SIG=%4
if "%EDGE_SIG%"=="." set EDGE_SIG=
if "%EDGE_SIG%"=="" set EDGE_SIG=10

set BLUR_SIG=%5
if "%BLUR_SIG%"=="." set BLUR_SIG=
if "%BLUR_SIG%"=="" set /A BLUR_SIG=%EDGE_SIG%/2

set BLEND_PC=%6
if "%BLEND_PC%"=="." set BLEND_PC=
if "%BLEND_PC%"=="" set BLEND_PC=100

set COLSP=%7
if "%COLSP%"=="." set COLSP=

set TMPDIR=\temp\
set TMPGRAY=%TMPDIR%abs_gray.miff
set TMPMAG=%TMPDIR%abs_gray_mag.miff

if /I "%BLR_AT%"=="E" (
  set sNEG=
) else if /I "%BLR_AT%"=="N" (
  set sNEG=-channel RGB -negate +channel
) else (
  echo Bad BLR_AT [%BLR_AT%] should be E or N.
  exit /B 1

if "%absMSK_PROC%"=="" set absMSK_PROC=-contrast-stretch 45%%x45%%
if "%absMSK_PROC%"=="." set absMSK_PROC=

if "%absRD_MASK%"=="" (
  set sRD_MASK=%TMPMAG% -auto-level %absMSK_PROC%
) else (
  set sRD_MASK=%absRD_MASK%

if "%absWR_MASK%"=="" (
  set sWR_MASK=
) else (
  set sWR_MASK=+write %absWR_MASK%

for /F "usebackq" %%L in (`%IMG7%magick ^
  %INFILE% ^
  -format "PREV_COLSP=%%[colorspace]" +write info: ^
  -colorspace Gray ^
  %absMSK_PRE% ^
  %TMPGRAY%`) do set %%L
if "%PREV_COLSP%"=="" exit /B 1

if "%COLSP%" NEQ "" if /I "%COLSP%" NEQ "%PREV_COLSP%" (
  set COLSP_IN=-colorspace %COLSP% -set colorspace sRGB
  set COLSP_OUT=-set colorspace %COLSP% -colorspace %PREV_COLSP%

call %PICTBAT%slopeXYblMag ^

%IMG7%magick ^
  %INFILE% ^
  %COLSP_IN% ^
  ( +clone ^
    ( %sRD_MASK% ^
      %sWR_MASK% ^
      %sNEG% ^
    ) ^
    -compose Blur ^
    -set option:compose:args %BLUR_SIG%x%BLUR_SIG% ^
    -composite ^
  ) ^
  -compose Blend -define compose:args=%BLEND_PC% -composite ^
  +depth ^
  -depth 32 ^
  -define "quantum:format=floating-point" ^

if ERRORLEVEL 1 exit /B 1

call echoRestore

@endlocal & set absOUTFILE=%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
Copyright: (C) 1999 ImageMagick Studio LLC
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
Copyright: (C) 1999-2021 ImageMagick Studio LLC
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 adapblrshp.h1. To re-create this web page, execute "procH1 adapblrshp".

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 29-Aug-2022 11:47:08.

Copyright © 2022 Alan Gibson.