Menu ▾ ▴

#15 Blur: should the centre point be part of the average?

closed
nobody
15 hours ago
2026-09-27
No

BlurBuffer and BlurBufferMask in src/u_dzGraphicsUtils.pas average a pixel with its eight neighbours (div 9). The TODO at both asks whether the pixel itself should be left out, which might blur better (the divisor would then be 8).

From the TODO scan in issue 12 of dzlib's ISSUES.md.

Related

Bug Reports: #26
Feature Requests: #26

Discussion

  • Thomas Mueller

    Thomas Mueller - 2 days ago

    Analysis (no code changed yet):

    Leaving out the centre would not blur better. The 3x3 average including the centre is the standard box blur. Without the centre each pixel loses its own share and takes only its neighbours, which is a different filter with visible artifacts:

    • A single bright pixel on black becomes a soft 3x3 spot with the centre, but a hollow ring without it: its own position drops to 0 and the brightness moves to the eight neighbours. Further passes move the ring around rather than smoothing it.
    • Alternating rows: both versions invert the contrast (a known weakness of the 3x3 box filter), but without the centre it gets worse, -1/2 instead of -1/3 of the original contrast.
    • A checkerboard is the one pattern where leaving the centre out is better: it is removed completely instead of keeping 1/9 of its contrast. Ordinary pictures do not look like that.

    A better blur would go the other way: weight the centre more, e.g. the binomial kernel

    1 2 1
    2 4 2   / 16
    1 2 1
    

    which approximates a Gaussian blur without the contrast inversion. It changes how existing blurs look, so it would be a change of behaviour.

    A possible problem with div 9: it rounds down. On uniform areas nothing is lost (9 * 100 div 9 = 100), but at edges and in noise every step loses just under half a brightness level on average. TBitmap_BlurRect and TBitmap_BlurEllipse run the filter twice per pass, so with many passes blurred areas could get noticeably darker. Not measured yet; rounding instead, (Sum + 4) div 9, would avoid it.

     
  • Thomas Mueller

    Thomas Mueller - 16 hours ago

    Measured on two real photos (4096x2304): two faces blurred with TBitmap_BlurEllipse, two licence plates with TBitmap_BlurRect, plus five variants of the 3x3 step in a test program. A reimplementation of the current code gave bit-identical results to dzlib, so the comparison is fair.

    Variant 3x3 step Result
    A (current) box, div 9 rounding down darkens ~0.4 levels per step: plate brightness 80 -> 60 / 17 / 4 after 50 / 200 / 800 passes, faces 135 -> 1 at 800. Unrecognisable only because it turns black
    B box, rounded no darkening, but stalls: between 200 and 800 passes only 6% of the plate pixels still change, the front plate stays readable at 800
    C box without centre, rounded keeps blurring but brightens (80 -> 94 at 800), plates partly readable
    D binomial 1 2 1 / 2 4 2 / 1 2 1, rounded stalls even earlier than B (0.5% still changing), plate readable at 800
    E box, values kept x256 in Integer buffers keeps blurring, no drift, faces and plates unrecognisable at 800
    F binomial, values kept x256 like E

    So the centre question is settled: leaving it out (C) is worse. The real problem is that the intermediate values are bytes: rounding down darkens, rounding to nearest freezes once a gradient is smooth. Keeping more precision (E/F) fixes both; box or binomial makes little difference at this scale.

    Also tried: a separable box blur with a radius, three passes with a running sum (nearly Gaussian, sigma ~ sqrt(r(r+1))), using the real neighbouring pixels. At r = 23 it matches E at 800 passes visually, and takes ~100 ms instead of ~2.5 s for the two faces. r = 12 left the front plate partly readable, r >= 20 did not.

    Follow-ups filed: the darkening/stalling as [bugs:#26], the radius-based blur as [#26].

     

    Related

    Bug Reports: #26
    Feature Requests: #26

  • Thomas Mueller

    Thomas Mueller - 15 hours ago

    Closed: the centre stays in the 3x3 average. Leaving it out blurs worse (variant C above). The darkening found while measuring is handled in [bugs:#26], the radius-based blur in [#26].

     

    Related

    Bug Reports: #26
    Feature Requests: #26


Log in to post a comment.