snibgo's ImageMagick pages

Windowed mean and standard deviation

Some methods: fast and slow, coarse and smooth.

Setting each pixel to the mean or standard deviation of the pixels in a surrounding window is a common requirement. For rectangular windows, IM has built-in operations, but the time is proportional to the window size so is slow for large windows.

A number of methods are given below. They fall into two groups:

  1. All pixels within a rectangular window have equal weight. This gives somewhat blocky results. ("Bad bokeh.")
  2. Pixels at the centre of the window have more weight than those at the edges. This gives smoother results. ("Good bokeh.")

For each method, we show: the mean and SD results; the auto-levelled SD and a portion of this, enlarged.

Sample input

set SRC=toes.png
toes.pngjpg

Mean and SD by "-statistic"

%IM%convert ^
  %SRC% ^
  ( +clone ^
    -statistic mean 21x21 ^
    +write wms_mn1.png +delete ) ^
  -statistic standard_deviation 21x21 ^
  wms_sd1.png
wms_mn1.pngjpg wms_sd1.pngjpg

For clarity, auto-level the SD.

%IM%convert ^
  wms_sd1.png ^
  -auto-level ^
  +write wms_sd1a.png ^
  -crop 100x100+16+100 +repage ^
  -scale 400%% ^
  wms_sd1as.png
wms_sd1a.pngjpg wms_sd1as.pngjpg

Blockiness is evident in the SD images.

Mean and SD by blur (capped window)

Specifying a radius and a large sigma caps the effect.

set BLR_RAD=10
set BLR_SIG=64000

%IM%convert ^
  %SRC% ^
  ( -clone 0 ^
    -evaluate Pow 2 ^
    -gaussian-blur %BLR_RAD%x%BLR_SIG% ^
  ) ^
  ( -clone 0 ^
    -gaussian-blur %BLR_RAD%x%BLR_SIG% ^
    +write wms_mnc1.png ^
    -evaluate Pow 2 ^
  ) ^
  -delete 0 ^
  -alpha off ^
  -compose MinusSrc -composite ^
  -evaluate Pow 0.5 ^
  wms_sdc1.png
wms_mnc1.pngjpg wms_sdc1.pngjpg

For clarity, auto-level the SD.

%IM%convert ^
  wms_sdc1.png ^
  -auto-level ^
  +write wms_sdc1a.png ^
  -crop 100x100+16+100 +repage ^
  -scale 400%% ^
  wms_sdc1as.png
wms_sdc1a.pngjpg wms_sdc1as.pngjpg

Mean and SD by integral image (cumulhisto)

This method is explained on the Integral images page.

call %PICTBAT%integMeanSd ^
  %SRC% wms_mni1.png wms_sdi1.png 21x21
wms_mni1.pngjpg wms_sdi1.pngjpg

For clarity, auto-level the SD.

%IM%convert ^
  wms_sdi1.png ^
  -auto-level ^
  +write wms_sdi1a.png ^
  -crop 100x100+16+100 +repage ^
  -scale 400%% ^
  wms_sdi1as.png
wms_sdi1a.pngjpg wms_sdi1as.pngjpg

Mean and SD by integral image (integim)

This method is explained on the Integral images page.

call %PICTBAT%iiMeanSd ^
  %SRC% wms_mnii1.png wms_sdii1.png 21x21
wms_mnii1.pngjpg wms_sdii1.pngjpg

For clarity, auto-level the SD.

%IM%convert ^
  wms_sdii1.png ^
  -auto-level ^
  +write wms_sdii1a.png ^
  -crop 100x100+16+100 +repage ^
  -scale 400%% ^
  wms_sdii1as.png
wms_sdii1a.pngjpg wms_sdii1as.pngjpg

Mean and SD by blur (Gaussian window)

The windowed mean from "-blur" has pixels near the center of the window weighted more than those near the edge.

set BLR_RAD=0
set BLR_SIG=6.6

%IM%convert ^
  %SRC% ^
  ( -clone 0 ^
    -evaluate Pow 2 ^
    -blur %BLR_RAD%x%BLR_SIG% ^
  ) ^
  ( -clone 0 ^
    -blur %BLR_RAD%x%BLR_SIG% ^
    +write wms_mnb1.png ^
    -evaluate Pow 2 ^
  ) ^
  -delete 0 ^
  -alpha off ^
  -compose MinusSrc -composite ^
  -evaluate Pow 0.5 ^
  wms_sdb1.png
wms_mnb1.pngjpg wms_sdb1.pngjpg

For clarity, auto-level the SD.

%IM%convert ^
  wms_sdb1.png ^
  -auto-level ^
  +write wms_sdb1a.png ^
  -crop 100x100+16+100 +repage ^
  -scale 400%% ^
  wms_sdb1as.png
wms_sdb1a.pngjpg wms_sdb1as.pngjpg

This "Gaussian standard deviation" is a useful edge detector, so we implement it as a script. See Gain and bias: Gaussian standard deviation.

Mean and SD by resize

The script resizeMeanSd.bat works by resizing the image down, then back up again. For heavy blurs, this is fast, but creates aliased results, a stepped appearance.

set BLR_SIG=6.6

call %PICTBAT%resizeMeanSd ^
  %SRC% wms_mnr1.png wms_sdr1.png ^
  %BLR_SIG%x%BLR_SIG%
wms_mnr1.pngjpg wms_sdr1.pngjpg

For clarity, auto-level the SD.

%IM%convert ^
  wms_sdr1.png ^
  -auto-level ^
  +write wms_sdr1a.png ^
  -crop 100x100+16+100 +repage ^
  -scale 400%% ^
  wms_sdr1as.png
wms_sdr1a.pngjpg wms_sdr1as.pngjpg

The sloping line near the bottom of both SD images has a stepped appearance. This effect is reduced by using a cubic, quadratic or spline filter. We show the mean, and the auto-levelled standard deviation.

set rmnsdFILTER=Cubic

call %PICTBAT%resizeMeanSd ^
  %SRC% wms_mnr2.png wms_sdr2.png ^
  %BLR_SIG%x%BLR_SIG%

%IM%convert ^
  wms_sdr2.png ^
  -auto-level ^
  wms_sdr2.png
wms_mnr2.pngjpg wms_sdr2.pngjpg
set rmnsdFILTER=Quadratic

call %PICTBAT%resizeMeanSd ^
  %SRC% wms_mnr3.png wms_sdr3.png ^
  %BLR_SIG%x%BLR_SIG%

%IM%convert ^
  wms_sdr3.png ^
  -auto-level ^
  wms_sdr3.png
wms_mnr3.pngjpg wms_sdr3.pngjpg
set rmnsdFILTER=Spline

call %PICTBAT%resizeMeanSd ^
  %SRC% wms_mnr4.png wms_sdr4.png ^
  %BLR_SIG%x%BLR_SIG%

%IM%convert ^
  wms_sdr4.png ^
  -auto-level ^
  wms_sdr4.png

set rmnsdFILTER=
wms_mnr4.pngjpg wms_sdr4.pngjpg

Comparisons

Compare Statistic with Circular blur and Integral (cumulhisto):

%IM%compare -metric RMSE ^
  wms_mn1.png wms_mnc1.png NULL: 
0.707824 (1.08007e-005)
%IM%compare -metric RMSE ^
  wms_mn1.png wms_mni1.png NULL: 
0.707234 (1.07917e-005)

These three methods are virtually identical.

Compare the two integral images methods:

%IM%compare -metric RMSE ^
  wms_mni1.png wms_mnii1.png NULL: 
360.406 (0.00549944)

This difference isn't trivial. The reason is that the methods treat edges differently. If we compare just the central 75% of each:

%IM%convert ^
  wms_mni1.png ^
  wms_mnii1.png ^
  -gravity Center -crop 75x75%%+0+0 +repage ^
  -metric RMSE -compare -format %%[distortion] ^
  info: 
3.71238e-006

Compare Statistic with Gaussian blur:

%IM%compare -metric RMSE ^
  wms_mn1.png wms_mnb1.png NULL: 
384.92 (0.00587351)

Compare Statistic with Resize:

%IM%compare -metric RMSE ^
  wms_mn1.png wms_mnr1.png NULL: 
843.858 (0.0128765)

Compare Statistic with Cubic Resize:

%IM%compare -metric RMSE ^
  wms_mn1.png wms_mnr2.png NULL: 
1841.84 (0.0281047)

Large images

We compare the performance of some methods using the same large blur on a large image.

set LGE_SRC=AGA_1434_gms.tiff

%IM%identify %LGE_SRC% 
AGA_1434_gms.tiff TIFF 4924x7378 4924x7378+0+0 16-bit sRGB 174.1MB 0.000u 0:00.000
Method Time
d hh:mm:ss

"-statistic" method.

%IM%convert ^
  %LGE_SRC% ^
  -statistic mean 61x61 ^
  wms_ls_stat.miff
0 00:51:50

Integral method (using cumulhisto).

call %PICTBAT%integMeanSd ^
  %LGE_SRC% wms_ls_mni1.miff . 61x61
0 00:02:25

Blur method.

set BLR_RAD=0
set BLR_SIG=19.8

%IM%convert ^
  %LGE_SRC% ^
  -blur %BLR_RAD%x%BLR_SIG% ^
  wms_sdb2.miff
0 00:00:23

Integral method (using integim).

call %PICTBAT%iiMeanSd ^
  %LGE_SRC% wms_ls_mnii1.miff . 61x61
0 00:00:09

Resize method.

set BLR_SIG=19.8

call %PICTBAT%resizeMeanSd ^
  %LGE_SRC% wms_mnr1.miff . %BLR_SIG%x%BLR_SIG%
0 00:00:04

There is an enormous variation in speed: a factor of 1000.

Conclusions

For small images where performance isn't an issue:

For large blurs of large images, where performance is an issue:

Scripts

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

resizeMeanSd.bat

rem From image %1
rem write windowed mean %2
rem and standard deviation %3
rem with window size %4
rem using the resize method.
@rem
@rem Also uses:
@rem   rmnsdFILTER
@rem


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

@setlocal enabledelayedexpansion

@call echoOffSave

call %PICTBAT%setInOut %1 rmnsd

set OUTMEAN=%2
if "%OUTMEAN%"=="." set OUTMEAN=
if "%OUTMEAN%"=="" set OUTMEAN=

set OUTSD=%3
if "%OUTSD%"=="." set OUTSD=
if "%OUTSD%"=="" set OUTSD=

set WINDIMS=%4

set WW=
for /F "usebackq" %%L in (`%IM%identify ^
  -format "WW=%%w\nHH=%%h\n" ^
  %INFILE%`) do set %%L
if "%WW%"=="" exit /B 1


call parseXxY2 %WW% %HH% rmnsd %WINDIMS%

echo %0: rmnsd_X=%rmnsd_X% rmnsd_Y=%rmnsd_Y%

for /F "usebackq" %%L in (`%IM%identify ^
  -format "RW=%%[fx:int(%WW%/2/%rmnsd_X%+0.5)]\nRH=%%[fx:int(%HH%/2/%rmnsd_Y%+0.5)]\n" ^
  xc:`) do set %%L

if %RW%==0 set RW=1
if %RH%==0 set RH=1

echo %0: rmnsd_X=%rmnsd_X% rmnsd_Y=%rmnsd_Y% RW=%RW% RH=%RH%

if "%OUTMEAN%"=="" (
  set wrOUTMEAN=
) else (
  set wrOUTMEAN=+write %OUTMEAN%
)

if "%rmnsdFILTER%"=="" (
  set sFILT=
) else (
  set sFILT=-filter %rmnsdFILTER%
)

if "%OUTSD%"=="" (

  %IM%convert ^
    %INFILE% ^
    %sFILT% ^
    -resize "%RW%x%RH%^!" -resize "%WW%x%HH%^!" ^
    %wrOUTMEAN% ^
    NULL:

) else (

  %IM%convert ^
    %INFILE% ^
    %sFILT% ^
    ^( -clone 0 ^
       -evaluate Pow 2 ^
       -resize "%RW%x%RH%^!" -resize "%WW%x%HH%^!" ^
    ^) ^
    ^( -clone 0 ^
       -resize "%RW%x%RH%^!" -resize "%WW%x%HH%^!" ^
       %wrOUTMEAN% ^
       -evaluate Pow 2 ^
    ^) ^
    -delete 0 ^
    -alpha off ^
    -compose MinusSrc -composite ^
    -evaluate Pow 0.5 ^
    %OUTSD%
)

call echoRestore

endlocal

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

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


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 11-June-2017.

Page created 24-Jul-2017 22:46:19.

Copyright © 2017 Alan Gibson.