snibgo's ImageMagick pages

Selective blur

We sometimes want to vary the amount of blur across an image, perhaps by using a mask: maximum blur where the mask is white, and no blur where the mask is black.

Blurring involves (in a sense) moving pixels. Thus the common technique of blending two images for a selective effect creates two superimposed images, a "ghosting". This may be undersirable and certainly creates a different effect. Methods shown here avoid this ghosting.

See also Image Mapped Effects: Variable Blur Mapping.

Binary masking

Create an artificial source of a repeating pattern with detail down to the pixel level.

%IMG7%magick ^
  xc:Black xc:gray(25%%) -append +repage ^
  ( +clone -rotate 180 ) ^
  +append +repage ^
  ( +clone -negate ) -append +repage ^
  ( +clone -rotate 180 ) ^
  +append +repage ^
  ( +clone -fill Red -colorize 50 ) -append +repage ^
  ( +clone -rotate 180 ) ^
  +append +repage ^
  ( +clone -fill Lime -colorize 50 ) -append +repage ^
  ( +clone -rotate 180 ) ^
  +append +repage ^
  ( +clone -fill Blue -colorize 50 ) -append +repage ^
  ( +clone -rotate 180 ) ^
  +append +repage ^
  ( +clone -modulate 100,50,100 ) -append +repage ^
  ( +clone -rotate 180 ) ^
  +append +repage ^
  ( +clone -modulate 100,100,50 ) -append +repage ^
  ( +clone -rotate 180 ) ^
  +append +repage ^
  ( +clone ) -append +repage ^
  ( +clone -rotate 180 ) ^
  +append +repage ^
  sb_src.png

set SRC=sb_src.png

for /F %%L in ('%IMG7%magick identify ^
  -format "WW=%%w\nHH=%%h" ^
  %SRC%') do set %%L
sb_src.png

Blur the entire source.

set BLUR_SIG=10

%IMG7%magick ^
  %SRC% ^
  -blur 0x%BLUR_SIG% ^
  sb_b1.png
sb_b1.png

Create a mask for the effect.

%IMG7%magick ^
  -size %WW%x%HH% xc:Black ^
  -draw "fill White rectangle 100,100 199,199" ^
  sb_rect_mask.png
sb_rect_mask.png

Apply the mask opacity to the blur.

%IMG7%magick ^
  %SRC% ^
  sb_b1.png ^
  sb_rect_mask.png ^
  -composite ^
  sb_rectmaskedBlur.png

The effect is analogous to placing a ground-glass screen over part of the image.

sb_rectmaskedBlur.pngjpg

We can soften the edge of the effect by blurring the mask before applying its opacity.

%IMG7%magick ^
  %SRC% ^
  sb_b1.png ^
  ( sb_rect_mask.png -blur 0x4 ) ^
  -composite ^
  sb_rectmaskedBlur2.png
sb_rectmaskedBlur2.pngjpg

Blur x or y

Blur horizontally.

%IMG7%magick ^
  sb_src.png ^
  -morphology Convolve Blur:0x5 ^
  sb_blr_x.png
sb_blr_x.png

Blur vertically.

%IMG7%magick ^
  sb_src.png ^
  -morphology Convolve Blur:0x5,90 ^
  sb_blr_y.png
sb_blr_y.png

Varying blur sigma

The sequence "-compose Blur -set option:compose:args SxxSy -composite", where Sx and Sy are numbers representing width and height of an elliptical Gaussian blur, creates a blur of dimension that depends on values in the Src image.

When the Src is black, the Dest image is more or less unchanged (eg RMSE 0.4%). When the Src is white and Sx == Sy, the effect is virtually identical to -blur 0xS (eg RMSE 0.007%).

Create a mask for the effect.

%IMG7%magick ^
  -size %WW%x%HH% radial-gradient: ^
  sb_mask.png

This mask graduates to the corners, not the edges.

See Gradients Cookbook for other possibilities.

sb_mask.png

Apply the mask opacity to the blur.

%IMG7%magick ^
  %SRC% ^
  sb_b1.png ^
  sb_mask.png ^
  -composite ^
  sb_maskedBlur.png

Detail is still visible except for a handful of pixels at the centre.

Although blur has occured elsewhere, it is visually overwhelmed by the strong detail in the image.

sb_maskedBlur.pngjpg

Use "-compose blur" to truly vary the blur.

%IMG7%magick ^
  %SRC% ^
  sb_mask.png  ^
  -compose Blur ^
  -set option:compose:args %BLUR_SIG%x%BLUR_SIG% -composite ^
  sb_maskedBlur2.png

The blur at the centre is now full, and it correctly decreases towards the edges.

sb_maskedBlur2.pngjpg

Mask the blur with a cheaper method.

%IMG7%magick ^
  sb_mask.png -write mpr:MASK ^
  %SRC% ^
  +swap ^
  -compose Blur ^
  -set option:compose:args %BLUR_SIG%x0 -composite ^
  mpr:MASK ^
  -set option:compose:args 0x%BLUR_SIG% -composite ^
  sb_maskedBlur3.png

Compare the methods:

%IMG7%magick compare ^
  -metric RMSE ^
  sb_maskedBlur2.png sb_maskedBlur3.png ^
  NULL: 
cmd /c EXIT /B 0
1719.82 (0.0262428)
sb_maskedBlur3.pngjpg

By sending these two versions as layers in a Gimp image (see Gimp and IM) ...

call %PICTBAT%toGimp sb_maskedBlur2.png sb_maskedBlur3.png

... and blinking one layer on and off, we can see the difference is mostly in the corners, where there should be no blur. The cheaper method has introduced a small blur in these areas.

If we have a colour photograph, we can blur it with a radius proportional to the distance (in RGB space) each pixel is from a given colour.

Source photograph: toes.png.

set SRC_TOES=toes.png

%IMG7%magick identify -format %%[fx:mean] %SRC_TOES% 
0.488162
toes.png

Colour distance. See Making a picture greyscale.

Finds the distance to the closest of two colours, being the toes in shadow and the toes in sunshine.

call %PICTBAT%hashCol %SRC_TOES% -crop 10x10+116+86

set ONE_HASH_COL=%HASH_COL%

call %PICTBAT%hashCol %SRC_TOES% -crop 10x10+225+116

"%IMG7%magick" ^
  %SRC_TOES% ^
  ^( -clone 0 ^
    ^( +clone -fill %ONE_HASH_COL% -colorize 100 ^) ^
    -compose Difference -composite ^
    -grayscale RMS ^
  ^) ^
  ^( -clone 0 ^
    ^( +clone -fill %HASH_COL% -colorize 100 ^) ^
    -compose Difference -composite ^
    -grayscale RMS ^
  ^) ^
  -delete 0 ^
  -compose Darken -composite ^
  -auto-level ^
  sb_toes_cd.png
sb_toes_cd.pngjpg

Blur, applying the "colour distance" mask.

%IMG7%magick ^
  %SRC_TOES% ^
  sb_toes_cd.png  ^
  -compose Blur ^
  -set option:compose:args %BLUR_SIG%x%BLUR_SIG% -composite ^
  sb_toes_cd_bld.png

%IMG7%magick identify -format %%[fx:mean] sb_toes_cd_bld.png 
0.497378
sb_toes_cd_bld.pngjpg

Repeat the above, blurring in RGB colorspace.

%IMG7%magick ^
  %SRC_TOES% ^
  -set colorspace sRGB -colorspace RGB ^
  sb_toes_cd.png  ^
  -compose Blur ^
  -set option:compose:args %BLUR_SIG%x%BLUR_SIG% -composite ^
  -set colorspace RGB -colorspace sRGB ^
  sb_toes_cd_bldL.png

%IMG7%magick identify -format %%[fx:mean] sb_toes_cd_bldL.png 
0.502436
sb_toes_cd_bldL.pngjpg

The grass has been heavily blurred; the toes are blurred far less. We could apply a gamma < 1 to the mask, darkening it, reducing the blurring of the colours fairly close to the toes.

Green from grass has not spread into the pink toes. However, pink from the toes has spread into the green grass, reducing local contrast of both tone and hue. We can use the mask to make the toes (nearly) transparent so they don't participate (much) in the blur, then turn alpha off after the blur. See also Masking: read mask, which describes a technique for binary masks. I want continuous (not binary) masking, so I exclude the final step.

Blurring tends to remove outliers, so I also "-auto-level".

Apply the "colour distance" mask with read-mask.

%IMG7%magick ^
  %SRC_TOES% ^
  sb_toes_cd.png ^
  ( -clone 0-1 ^
    -alpha off -compose CopyOpacity -composite ^
  ) ^
  ( -clone 1 ) ^
  -compose Blur ^
  -set option:compose:args %BLUR_SIG%x%BLUR_SIG% -composite ^
  -alpha off ^
  sb_toes_cd_bld2.png

%IMG7%magick identify -format %%[fx:mean] sb_toes_cd_bld2.png 
0.494536
sb_toes_cd_bld2.pngjpg

Repeat the above, transforming in RGB colorspace.

%IMG7%magick ^
  %SRC_TOES% ^
  -set colorspace sRGB -colorspace RGB ^
  sb_toes_cd.png ^
  ( -clone 0-1 ^
    -alpha off -compose CopyOpacity -composite ^
  ) ^
  ( -clone 1 ) ^
  -compose Blur ^
  -set option:compose:args %BLUR_SIG%x%BLUR_SIG% -composite ^
  -alpha off ^
  -set colorspace RGB -colorspace sRGB ^
  sb_toes_cd_bld2L.png

%IMG7%magick identify -format %%[fx:mean] sb_toes_cd_bld2L.png 
0.500174
sb_toes_cd_bld2L.pngjpg

The result looks sharper as there is more contrast around the toes.

toes.png was cropped from a converted 14-bit camera raw file with minimal processing. An 8-bit/channel from the in-camera JPEG subjected to the same processing shows horrible quantum effects.

As we have a mask, we can use it to mask the unsharp operation so it will affect mostly light areas (the toes).

Sharpen.

%IMG7%magick ^
  sb_toes_cd_bld2.png ^
  -write-mask sb_toes_cd.png ^
  -unsharp 0x2 ^
  +write-mask ^
  sb_toes_cd_bld2u.png
sb_toes_cd_bld2u.pngjpg

Adaptive blur

The sharpness of the input image can modulate the blur. Here, we blur most where the input has the smallest slope.

This is like the built-in -adaptive-blur, but with greater control.

Detect edges.

call %PICTBAT%slopeXYbl ^
  %SRC_TOES% sb_slp.miff %BLUR_SIG%

call %PICTBAT%slopeXYMag ^
  sb_slp.miff sb_slp.miff

:: The following works est with HDRI.
::
%IMG7%magick ^
  sb_slp.miff ^
  -grayscale RMS -level 10%%,30%% ^
  -negate ^
  sb_slp_edge.png
sb_slp_edge.pngjpg

Modulate blur by the edge image.

%IMG7%magick ^
  %SRC_TOES% ^
  sb_slp_edge.png ^
  -compose Blur ^
  -set option:compose:args %BLUR_SIG%x%BLUR_SIG% -composite ^
  -alpha off ^
  sb_toes_slp_bld.png
sb_toes_slp_bld.pngjpg

If we don't negate the edge image, we get an "adaptive sharpen" effect.

Toytown effect

We can leave a horizontal line (or band) sharp with progressive blur above and below the line.

Make a "toytown" mask. See Clut Cookbook for the fx.

%IMG7%magick ^
  -size 1x%HH% gradient: ^
  -fx "VV=u*1.5;VV<1?VV:2-VV" ^
  -negate ^
  -scale "%WW%x%HH%^!" ^
  sb_mask2.png
sb_mask2.png

Apply the "toytown" mask.

%IMG7%magick ^
  %SRC% ^
  sb_mask2.png  ^
  -compose Blur ^
  -set option:compose:args %BLUR_SIG%x%BLUR_SIG% -composite ^
  sb_masked2Blur.png
sb_masked2Blur.pngjpg

Running a toytown ("Tilt-shift") mask on a 7500x5000 pixel image took 354 seconds for the true method and 103 seconds for the cheaper method. The RMSE was 912.454 (0.0139231).

We can also use integral images:

Apply the "toytown" mask.

%IM7DEV%magick ^
  %SRC% ^
  -process integim ^
  sb_mask2.png ^
  -process 'deintegim2 window %BLUR_SIG%x%BLUR_SIG%' ^
  sb_masked2Blur_ii.png
sb_masked2Blur_ii.pngjpg

Use a larger window:

%IM7DEV%magick ^
  %SRC% ^
  -process integim ^
  sb_mask2.png ^
  -process 'deintegim2 window %%[fx:3*%BLUR_SIG%]x%%[fx:3*%BLUR_SIG%]' ^
  sb_masked2Blur_ii2.png
sb_masked2Blur_ii2.pngjpg

Varying blur x- and y-sigmas

By using different values in the red and green channels of the mask, we can independently vary the blur amounts in the x and y directions.

Make an x-y mask.

%IMG7%magick ^
  -size %HH%x%WW% gradient: -rotate 90 ^
  -size %WW%x%HH% gradient: ^
  xc:black ^
  -combine ^
  sb_maskxy.png
sb_maskxy.png

Apply the x-y mask.

%IMG7%magick ^
  %SRC% ^
  sb_maskxy.png  ^
  -compose Blur ^
  -set option:compose:args %BLUR_SIG%x%BLUR_SIG% -composite ^
  sb_maskedxy.png

There is a visual problem top-left and bottom-right.

sb_maskedxy.pngjpg

Apply the gamma-changed x-y mask.

%IMG7%magick ^
  %SRC% ^
  ( sb_maskxy.png -gamma 0.5 ) ^
  -compose Blur ^
  -set option:compose:args %BLUR_SIG%x%BLUR_SIG% -composite ^
  sb_maskedxy2.png
sb_maskedxy2.pngjpg

Varying blur angle

By supplying two extra arguments, representing start and end angles, the blue channel of the mask will vary the rotation of the blur. This is pointless unless the x- and y-sigmas are different. In this section, we use %BLUR_SIG% for the x-sigma and zero for the y-sigma.

We first illustrate the effect with solid colours in the mask. The red and green channels are given values of 100%.

Blue channel = 0.

%IMG7%magick ^
  %SRC% ^
  ( +clone -fill rgb(100%%,100%%,0) -colorize 100 ) ^
  -compose Blur ^
  -set option:compose:args %BLUR_SIG%x0+0+360 -composite ^
  sb_solmskang1.png

This gives a horizontal blur.

sb_solmskang1.pngjpg

Blue channel = 12.5%.

%IMG7%magick ^
  %SRC% ^
  ( +clone -fill rgb(100%%,100%%,12.5%%) -colorize 100 ) ^
  -compose Blur ^
  -set option:compose:args %BLUR_SIG%x0+0+360 -composite ^
  sb_solmskang2.png

The blur is rotated clockwise by 12.5% of a complete revolution, 45°.

sb_solmskang2.pngjpg

Blue channel = 25%.

%IMG7%magick ^
  %SRC% ^
  ( +clone -fill rgb(100%%,100%%,25%%) -colorize 100 ) ^
  -compose Blur ^
  -set option:compose:args %BLUR_SIG%x0+0+360 -composite ^
  sb_solmskang3.png

The blur is rotated clockwise by 25% of a complete revolution, 90°.

sb_solmskang3.pngjpg

We now show effects where the mask is not a solid colour.

Create a varying-angle mask.

for /F %%i in ('%IMG7%magick identify ^
  -format "%%[fx:max(%WW%,%HH%)/2]" ^
  xc:') do set RMAX=%%i

set /A POL_WI=RMAX*3
set /A POL_HT=RMAX

%IMG7%magick ^
  -size %WW%x%HH% radial-gradient: -negate ^
  ( +clone ) ^
  ( -size %POL_HT%x%POL_WI% gradient: ^
    -rotate 90 ^
    +distort Polar ^"%RMAX%^" ^
    +repage -flop ^
    -gravity center -crop %WW%x%HH%+0+0 +repage ^
  ) ^
  -combine ^
  sb_maskang.png
sb_maskang.pngjpg

Apply the varying-angle mask.

%IMG7%magick ^
  %SRC% ^
  sb_maskang.png ^
  -compose Blur ^
  -set option:compose:args %BLUR_SIG%x0+0+360 -composite ^
  sb_maskedang.png

As radial-gradient continues to edges not corners,
the blur varies to the edges not corners.

sb_maskedang.pngjpg

The same but reversing the x and y sigmas.

%IMG7%magick ^
  %SRC% ^
  sb_maskang.png ^
  -compose Blur ^
  -set option:compose:args 0x%BLUR_SIG%+0+360 -composite ^
  sb_maskedang2.png

As radial-gradient continues to edges not corners,
the blur varies to the edges not corners.

sb_maskedang2.pngjpg

We can use the "-rotational-blur" operator. (In v6, this was misnamed "-radial-blur".) The parameter is in degrees.

%IMG7%magick ^
  %SRC% ^
  -rotational-blur 10 ^
  sb_radb.png
sb_radb.pngjpg

We can do a round-trip depolar/polar.

%IMG7%magick ^
  %SRC% ^
  -virtual-pixel Edge ^
  -set option:distort:scale 4   -distort DePolar -1 ^
  -virtual-pixel Tile ^
  -scale "5%%x100%%^!" ^
  -filter Gaussian -resize "2000%%x100%%^!" +filter ^
  -virtual-pixel HorizontalTile -background Black ^
  -set option:distort:scale .25 -distort Polar -1 ^
  sb_depol.png

But we have a sharp vertical line upwards from the centre.

sb_depol.png

Another round-trip depolar/polar.

%IMG7%magick ^
  %SRC% ^
  -virtual-pixel Edge ^
  -set option:distort:scale 4   -distort DePolar -1 ^
  -virtual-pixel Tile ^
  -resize "5%%x100%%^!" ^
  -filter Gaussian -resize "2000%%x100%%^!" +filter ^
  -virtual-pixel HorizontalTile -background Black ^
  -set option:distort:scale .25 -distort Polar -1 ^
  sb_depol2.png

We still have a sharp vertical line upwards from the centre.

sb_depol2.png

On depolar/polar, see the official Distorting: Polar Tricks and my own Polar distortions.

Blur fill

The blurFill.bat script can be regarded as a selective blur. For more details, see Filling holes: by blur. Here are some examples:

toes_holed.png

toes_holed.png
call %PICTBAT%blurFill ^
  toes_holed.png . sb_th1.png
sb_th1.png
call %PICTBAT%blurFill ^
  toes_holed.png 1+ sb_th1p.png
sb_th1p.png
call %PICTBAT%blurFill ^
  toes_holed.png . sb_th_mb1.png 1 225
sb_th_mb1.png
call %PICTBAT%blurFill ^
  toes_holed.png . sb_th_rb1.png 2 10
sb_th_rb1.png
call %PICTBAT%blurFill ^
  toes_holed.png . sb_th_rb2.png 2 45
sb_th_rb2.png

Blur directed by slope

As shown on the Slopes page, blurring can be in the direction of a slope, or perpendicular to that direction. The same method can make an unsharp mask for a sharpening process.

Input image: toes.png

toes.pngjpg

After a blur perpendicular to the slope at each pixel.

slp_bs_blr.jpg

slp_bs_blr.jpg

Previous result, sharpened with mask parallel to the slopes.

slp_bs_blr_sh.jpg

slp_bs_blr_sh.jpg

Similarly, we can modulate the blur by the slope magnitude.

Most blur where the slope is smallest.

slp_bs_blr2.jpg

slp_bs_blr2.jpg

All images on this page were created by the commands shown, (except that to conserve bandwidth some PNG files were converted to JPG), using:

%IMG7%magick identify -version
Version: ImageMagick 7.1.0-49 Q16-HDRI x64 7a3f3f1:20220924 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 (193331630)

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


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 29-Jan-2014.

Page created 16-Mar-2023 11:57:19.

Copyright © 2023 Alan Gibson.