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.
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 from scene radiance , global airlight , and transmission :
Transmission falls exponentially with depth, so estimating 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 , and per-patch minima estimate . Learned methods regress or directly, often jointly with depth.
Worked example
Observed , airlight , transmission . Recover . Division by small amplifies sensor noise, so implementations clamp above : distant pixels with 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 recoveredWatch 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 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 .
- Estimating transmission per pixel inverts the mix into true colours.
- Our pixel recovers from observed at .
- Dark channel statistics supply the classic transmission estimator.
- White surfaces and far-distance noise amplification are the two traps.