﻿

# Measuring blur

Some metrics for blurriness.

If we have a reference image and a blurred version, we can measure the difference (eg RMSE) caused by the blur. But we might not have a reference, merely a blurred version. Here are some ways of measuring blurriness of individual images.

We might use standard deviation, either of the entire image, or of the average (or maximum) SD of a sliding window. (Sliding window SD is fast when calculated by integral images). However, SD is independent of pixel locations. It measures contrast, either overall or within each window, but says nothing about whether the pixels are arranged to give a smooth gradient or sharp boundary.

A more complex method, described in a paper by Crete et al, is sometimes used. In a nutshell: we blur the image, and see how much it has changed. If it has changed a lot then it was sharp, but if it didn't change much then it was already blurred.

## The method

We take a grayscale image and its blur in one dimension, and the slope magnitude of each. This is a measure of high-freqency amplitude, that blurring attenuates. Subtract the slope magnitudes to get the variation. Where the variation is high, the blur has removed high frequencies. Where the variation is low or zero, blurring has not removed high frequencies so it was already blurred.

The paper gives an algorithm:

1. Make a grayscale of the input image, and call it F.
2. Blur F horizontally, using a sliding window mean of 9x1 pixels, and call it BHor.
3. Blur F vertically, using a sliding window mean of 1x9 pixels, and call it BVer.
4. From F, calculate the absolute difference between each pixel and the pixel above. Call this image D_FVer.
5. From F, calculate the absolute difference between each pixel and the pixel to the left. Call this image D_FHor.
6. From BVer, calculate the absolute difference between each pixel and the pixel above. Call this image D_BVer.
7. From BHor, calculate the absolute difference between each pixel and the pixel to the left. Call this image D_BHor.
8. Calculate D_FVer minus D_BVer, clamping negative values to zero. Call this image VVer. (But I think the authors intended to say D_VVer.)
9. Calculate D_FHor minus D_BHor, clamping negative values to zero. Call this image VHor. (But I think the authors intended to say D_VHor.)
10. Sum the pixels in D_FVer. Call this s_FVer.
11. Sum the pixels in D_FHor. Call this s_FHor.
12. Sum the pixels in D_VVer. Call this s_VVer.
13. Sum the pixels in D_VHor. Call this s_VHor.
14. Calculate b_FVer = (s_FVer - s_VVer) / s_FVer = 1 - (s_VVer / s_FVer).
15. Calculate b_FHor = (s_FHor - s_VHor) / s_FHor = 1 - (s_VHor / s_FHor).
16. Calculate blur_F = max (b_FVer, b_FHor). This is between 0.0 ("best quality") and 1.0 ("worst quality").

If we take the paper literally: VVer and VHor are calculated but never used; D_VVer and D_VHor are used but never calculated. So I suppose the prefix D_ has been accidentally omitted.

The blur can be done with "-statistic Mean" or "-process integim", giving slightly different results due to different treatment at edges.

Summing pixels could be done by "-process integim" and using the last pixel, but we need one sum divided by another sum from same-sized images, so instead we can use "-scale 1x1!" to get the means.

If the input images have pixel values in the range 0 to 100%, then all intermediate images are also in that range, so HDRI is not required to cope with OOG. If we use "-process integim", that needs HDRI. Also, we calculate differences between adjacent pixels, and these are small numbers so we could easily lose precision when we take the mean and then divide, so HDRI is a good idea.

The script measureBlur.bat implements this. Here are some examples, also showing some standard deviation (SD) statistics.

Note that:

• SD usually ranges from 0.0 to 0.5, but mb_score ranges from 0.0 to 1.0.
• SD and mb_scores go in opposite directions: a total blur has SD=0.0 but mb_score=1.0.
• SD is a measure of pixel values, irrespective of the pixel locations. mb_score is a measure of local contrast.

For each example, we calculate and show four statistics:

1. SD: the overall image SD as calculated by IM, the "unbiased estimate of the population standard deviation";
2. mb_score: the "No-Reference Perceptual Blur Metric";
3. wsdMeanSD: the mean value of the 9x9 sliding window SD;
4. wsdMaxSD: the maximum value of the 9x9 sliding window SD.

mb_score is calculated from intensity only. In these examples, the SD measurements are from all channels.

 ```%IMG7%magick ^ toes.png ^ -format "SD=%%[fx:standard_deviation]\n" ^ info: call %PICTBAT%measureBlur toes.png call %PICTBAT%iiWinSD toes.png 9x9 echo mb_score=%mb_score% echo wsdMeanSD=%wsdMeanSD% echo wsdMaxSD=%wsdMaxSD% ``` ```SD=0.153221 mb_score=0.4861897507057298 wsdMeanSD=0.0457052 wsdMaxSD=0.23287 ``` ```%IMG7%magick ^ toes.png ^ -blur 0x1 ^ +write mb_blr.png ^ -format "SD=%%[fx:standard_deviation]\n" ^ info: call %PICTBAT%measureBlur mb_blr.png call %PICTBAT%iiWinSD mb_blr.png 9x9 echo mb_score=%mb_score% echo wsdMeanSD=%wsdMeanSD% echo wsdMaxSD=%wsdMaxSD% ``` ```SD=0.150146 mb_score=0.6363555733577477 wsdMeanSD=0.0391658 wsdMaxSD=0.215996 ``` ```%IMG7%magick ^ toes.png ^ -unsharp 0x1 ^ +write mb_usm.png ^ -format "SD=%%[fx:standard_deviation]\n" ^ info: call %PICTBAT%measureBlur mb_usm.png call %PICTBAT%iiWinSD mb_usm.png 9x9 echo mb_score=%mb_score% echo wsdMeanSD=%wsdMeanSD% echo wsdMaxSD=%wsdMaxSD% ``` ```SD=0.155273 mb_score=0.4453474884603647 wsdMeanSD=0.0495723 wsdMaxSD=0.244698 ``` ```%IMG7%magick ^ toes.png ^ -blur 0x20 ^ +write mb_blr2.png ^ -format "SD=%%[fx:standard_deviation]\n" ^ info: call %PICTBAT%measureBlur mb_blr2.png call %PICTBAT%iiWinSD mb_blr2.png 9x9 echo mb_score=%mb_score% echo wsdMeanSD=%wsdMeanSD% echo wsdMaxSD=%wsdMaxSD% ``` ```SD=0.0960485 mb_score=0.993557116426337 wsdMeanSD=0.00652752 wsdMaxSD=0.0213354 ``` ```%IMG7%magick ^ toes.png ^ -colorspace Gray ^ -auto-gamma ^ -posterize 2 ^ +write mb_bw.png ^ -format "SD=%%[fx:standard_deviation]\n" ^ info: call %PICTBAT%measureBlur mb_bw.png call %PICTBAT%iiWinSD mb_bw.png 9x9 echo mb_score=%mb_score% echo wsdMeanSD=%wsdMeanSD% echo wsdMaxSD=%wsdMaxSD% ``` ```SD=0.498908 mb_score=0.1347237520981155 wsdMeanSD=0.161177 wsdMaxSD=0.5 ``` ```%IMG7%magick ^ toes.png ^ -fill #abc -colorize 100 ^ +write mb_col.png ^ -format "SD=%%[fx:standard_deviation]\n" ^ info: call %PICTBAT%measureBlur mb_col.png call %PICTBAT%iiWinSD mb_col.png 9x9 echo mb_score=%mb_score% echo wsdMeanSD=%wsdMeanSD% echo wsdMaxSD=%wsdMaxSD% ``` ```SD=0 mb_score=0.9999939798390173 wsdMeanSD=1.85585e-07 wsdMaxSD=1.70597e-06 ``` Sort the pixels in toes.png: ```%IM7DEV%magick ^ toes.png ^ -set option:WW %%w ^ -crop x1 +repage ^ +append +repage ^ -intensity Rec709Luminance ^ -process sortpixels ^ -flop ^ -crop %%[WW]x1 ^ -append +repage ^ +write mb_srt.png ^ -format "SD=%%[fx:standard_deviation]\n" ^ info: call %PICTBAT%measureBlur mb_srt.png call %PICTBAT%iiWinSD mb_srt.png 9x9 echo mb_score=%mb_score% echo wsdMeanSD=%wsdMeanSD% echo wsdMaxSD=%wsdMaxSD% ``` ```SD=0.153221 mb_score=0.7227744478141451 wsdMeanSD=0.0374802 wsdMaxSD=0.0720152 ``` The SD is unchanged from toes.png, but mb_score shows we have more blur. There is a fairly good correlation:

`wsdMaxSD * 2 = 1 - mb_score`

... roughly speaking.

## Scripts

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

### measureBlur.bat

```rem From image %1,
rem writes No-Reference Perceptual Blur Metric
rem to environment variable %2 [default mb_score].
rem %3 is prefix for debugging images. Default: no images.
@rem
@rem Reference: http://im.snibgo.com/measblur.htm

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

@setlocal enabledelayedexpansion

@call echoOffSave

call %PICTBAT%setInOut %1 mb

set ENVVAR=%2
if "%ENVVAR%"=="." set ENVVAR=
if "%ENVVAR%"=="" set ENVVAR=mb_score

set DBG_PREF=%3

if "%DBG_PREF%"=="" (
set WR_F=
set WR_BHOR=
set WR_BVER=
set WR_D_FVER=
set WR_D_FHOR=
set WR_D_BVER=
set WR_D_BHOR=
set WR_D_VVER=
set WR_D_VHOR=
) else (
set WR_F=+write %DBG_PREF%_f
set WR_BHOR=+write %DBG_PREF%_bhor
set WR_BVER=+write %DBG_PREF%_bver
set WR_D_FVER=+write %DBG_PREF%_d_fver
set WR_D_FHOR=+write %DBG_PREF%_d_fhor
set WR_D_BVER=+write %DBG_PREF%_d_bver
set WR_D_BHOR=+write %DBG_PREF%_d_bhor
set WR_D_VVER=+write %DBG_PREF%_d_vver
set WR_D_VHOR=+write %DBG_PREF%_d_vhor
)

set TMPDIR=\temp\

for /F "usebackq" %%L in (`%IMG7%magick ^
%INFILE% ^
-colorspace Gray ^
%WR_F% ^
+write mpr:F ^
^( +clone ^
-statistic Mean 9x1 ^
%WR_BHOR% ^
+write mpr:bhor ^
+delete ^) ^
^( +clone ^
-statistic Mean 1x9 ^
%WR_BVER% ^
+write mpr:bver ^
+delete ^) ^
^( +clone ^) ^
-geometry +0-1 ^
-compose Difference -composite ^
-geometry +0+0 ^
%WR_D_FVER% ^
+write mpr:d_fver ^
+delete ^
mpr:F ^( +clone ^) ^
-geometry -1+0 ^
-compose Difference -composite ^
-geometry +0+0 ^
%WR_D_FHOR% ^
+write mpr:d_fhor ^
+delete ^
mpr:bver ^( +clone ^) ^
-geometry +0-1 ^
-compose Difference -composite ^
-geometry +0+0 ^
%WR_D_BVER% ^
+write mpr:d_bver ^
+delete ^
mpr:bhor ^( +clone ^) ^
-geometry -1+0 ^
-compose Difference -composite ^
-geometry +0+0 ^
%WR_D_BHOR% ^
+write mpr:d_bhor ^
+delete ^
mpr:d_fver mpr:d_bver ^
-compose MinusSrc -composite -clamp ^
%WR_D_VVER% ^
+write mpr:d_vver ^
+delete ^
mpr:d_fhor mpr:d_bhor ^
-compose MinusSrc -composite -clamp ^
%WR_D_FHOR% ^
+write mpr:d_vhor ^
+delete ^
^( mpr:d_vver mpr:d_fver ^
-scale "1x1^!" ^
-compose DivideSrc -composite ^
^) ^
^( mpr:d_vhor mpr:d_fhor ^
-scale "1x1^!" ^
-compose DivideSrc -composite ^
^) ^
-compose Darken -composite ^
-negate ^
-precision 16 ^
-format "SCORE=%%[fx:mean]\n" ^
info:`) do set %%L

call echoRestore

@endlocal & set mbOUTFILE=%OUTFILE%& set %ENVVAR%=%SCORE%```

### iiWinSd.bat

```rem From image %1,
rem calculates mean and maximum windowed standard deviation.
rem using the process module integral image method.
rem %2 window size eg 3x3

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

@setlocal enabledelayedexpansion

@call echoOffSave

call %PICTBAT%setInOut %1 wsd

set WINDIMS=%2
if "%WINDIMS%"=="." set WINDIMS=
if "%WINDIMS%"=="" set WINDIMS=3x3

for /F "usebackq" %%L in (`%IM7DEV%magick ^
%INFILE% ^
^( -clone 0 ^
-evaluate Pow 2 ^
-process 'integim' ^
-process 'deintegim window %WINDIMS%' ^
^) ^
^( -clone 0 ^
-process 'integim' ^
-process 'deintegim window %WINDIMS%' ^
-evaluate Pow 2 ^
^) ^
-delete 0 ^
-alpha off ^
-compose MinusSrc -composite ^
-evaluate Pow 0.5 ^
-format "MeanSD=%%[fx:mean]\nMaxSD=%%[fx:maxima]\n" ^
info:`) do set %%L

call echoRestore

endlocal & set wsdMeanSD=%MeanSD%& set wsdMaxSD=%MaxSD%```

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

`%IM%identify -version`
```Version: ImageMagick 6.9.9-50 Q16 x64 2018-06-02 http://www.imagemagick.org
Visual C++: 180040629
Features: Cipher DPC Modules OpenMP
Delegates (built-in): bzlib cairo flif freetype gslib heic jng jp2 jpeg lcms lqr lzma openexr pangocairo png ps raw rsvg tiff webp xml zlib```
`%IMG7%magick -version`
```Version: ImageMagick 7.0.7-28 Q16 x64 2018-03-25 http://www.imagemagick.org
Visual C++: 180040629
Features: Cipher DPC HDRI Modules OpenMP
Delegates (built-in): bzlib cairo flif freetype gslib heic jng jp2 jpeg lcms lqr openexr pangocairo png ps raw 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 measblur.h1. To re-create this web page, execute "procH1 measblur".

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.