Skip to content
AI360Xpert
Beta

Image Dehazing

Image dehazing inverts the physics of fog, estimating how much haze sits in front of each pixel and dividing it back out to recover true colours.

Dehazing estimates per-pixel haze thickness and inverts the scattering equation to recover true scene colours
Dehazing estimates per-pixel haze thickness and inverts the scattering equation to recover true scene colours

Why Does This Exist?

Fog whitens with distance: far objects drown in scattered airlight while near ones punch through. Contrast stretching cannot fix this because the damage is depth-dependent, one global curve either leaves distance milky or crushes the foreground to black.

Dehazing models the physics instead. Each pixel mixes true scene colour with airlight according to its transmission, and estimating that transmission map turns fog removal into per-pixel arithmetic. The task map is image-to-image tasks, and the depth cue it exploits resembles the geometry in stereo vision.

Think of It Like This

Wiping steam from a shower mirror patch by patch

A steamed mirror clears unevenly: near the hot stream it stays fogged, at the edges it wipes clean. One wipe pressure for the whole mirror either leaves the centre milky or scratches the clear corners. You vary pressure by patch, reading each region's fog thickness as you go.

Transmission is the fog thickness per patch and airlight is the steam's own white. The method fails on white objects in white fog, marble under steam, where no measurement separates surface from obscurant.

How It Actually Works

The scattering model writes observed colour II from scene radiance JJ, global airlight AA, and transmission tt:

I(x)=J(x) t(x)+A (1−t(x)),J(x)=I(x)−At(x)+AI(x) = J(x) \, t(x) + A \, (1 - t(x)), \qquad J(x) = \frac{I(x) - A}{t(x)} + A

Transmission falls exponentially with depth, so estimating tt estimates relative depth for free. The dark channel prior observes that haze-free outdoor patches almost always contain some dark pixel in some channel; the brightest such pixels estimate AA, and per-patch minima estimate tt. Learned methods regress tt or JJ directly, often jointly with depth.

Worked example

Observed I=0.66I = 0.66, airlight A=0.9A = 0.9, transmission t=0.6t = 0.6. Recover J=(0.66−0.9×0.4)/0.6=(0.66−0.36)/0.6=0.30/0.6=0.5J = (0.66 - 0.9 \times 0.4)/0.6 = (0.66 - 0.36)/0.6 = 0.30/0.6 = 0.5. Division by small tt amplifies sensor noise, so implementations clamp tt above 0.10.1: distant pixels with t=0.05t = 0.05 would otherwise explode tiny errors twentyfold.

Code

Scattering inversion with the transmission safety clamp.

def dehaze_pixel(i: float, a: float, t: float, t_min: float = 0.1) -> float:    """Recover scene radiance, clamping thin transmission."""    t = max(t, t_min)  # distant pixels would amplify noise otherwise    return (i - a * (1 - t)) / t

print(dehaze_pixel(0.66, 0.9, 0.6))  # -> 0.5 true colour recovered

Watch Out For

White objects condemned as fog

The dark channel prior assumes darkness somewhere per patch. White buildings, snow, and bright walls violate it, so the estimator reads them as dense haze and over-darkens them into grey.

Detect bright-region violations and soften the prior there, or switch to learned estimators that handle them. Fog-white and object-white need different handling.

Noise explosion in far distance

At t=0.1t = 0.1 every sensor wobble multiplies tenfold. Distant treelines fizz with amplified chroma noise after aggressive dehazing, trading milk for static.

Clamp transmission and denoise after dehazing, not before. Report the clamp value; it sets the method's effective range.

The Quick Version

  • Fog mixes scene colour with airlight per the scattering equation I=Jt+A(1−t)I = Jt + A(1-t).
  • Estimating transmission per pixel inverts the mix into true colours.
  • Our pixel recovers J=0.5J = 0.5 from observed 0.660.66 at t=0.6t = 0.6.
  • Dark channel statistics supply the classic transmission estimator.
  • White surfaces and far-distance noise amplification are the two traps.