Removing Light Pollution Gradients Without Eating Your Target
Background extraction is the step most likely to damage an image while appearing to improve it. What goes wrong, and the one rule that prevents it.
More astrophotos are quietly damaged at the gradient removal step than anywhere else. The step is not especially hard. The trouble is that when it goes wrong the result still looks processed (flatter, cleaner, more deliberate), so the damage hides inside the improvement.
This post covers what the step does, the specific way it fails, and how to tell the difference.
What a gradient is
The sky is not evenly bright. A town on one horizon, the moon, twilight that has not quite gone, and your own optics delivering less light to the corners than the center all add up to a smooth, slowly varying brightness across the frame.
There are two reasons to remove it rather than live with it.
Stretching amplifies it. A gradient you can barely see in linear data dominates once you have pulled the faint end up. The stretch cannot tell sky glow from nebula, so it lifts both.
It corrupts the black point. The final background level is set by moving the image’s median to a target value. With a gradient there is no single sky level to move, so whatever value you pick, half the frame ends up too dark and the other half too bright.
The one rule
Fit the surface to sky, and only to sky.
That sounds obvious, and it is the whole difficulty. Background extraction samples the frame, decides what the sky level is at each sample, fits a smooth surface through the samples, and subtracts it.
A sample in the middle of a nebula reports the nebula’s brightness as the sky level at that point. Fit a surface through samples like that and it rises to meet your target. Subtract it and you have dug a bowl out of the object.
If you have ever run background extraction and got a dark halo around your galaxy, or a nebula that seems to sit in a shallow crater, this is what happened. The correction did exactly what it was told, and it was told to treat the target as sky.
A gentler setting will not fix it. You have to exclude the samples that sit on the object. Sky is the darkest thing in any frame, so take the low envelope of the sample levels and drop samples that sit well above it.
Why low order is the right choice
Once your samples are sky, the next question is how much freedom the fitted surface gets.
A first-degree surface is a plane, one tilt across the frame. Second degree handles a dome, such as vignetting or a single light source off one corner. Third degree handles a dome that is off center. Beyond that the model has enough freedom to start following your target.
That is the trade-off. Too little freedom and a complex gradient survives. Too much and the surface follows real structure and removes it.
Low order wins because light pollution is smooth. It is a broad glow from a source many kilometers away, with no edges, filaments or dust lanes. Anything in your frame with structure at that scale is signal. A polynomial that cannot express structure cannot eat your nebula, whatever the samples say, and that limitation is what you want.
I have tried the more flexible alternative: a surface smoothed through the sky samples with no assumed polynomial form. On a sky with two light domes pulling from opposite corners it did beat a cubic, leaving the background noticeably flatter. But at a bandwidth narrow enough to do that, it also followed a large nebula and dug the crater described above. Widening it until that could not happen left it worse than the cubic on the very sky it was meant for.
So flexibility you cannot control is not an improvement. Unless you have a real object mask, low order is the honest choice.
Subtract the slope, keep the level
One more detail separates a natural result from an obviously processed one.
The correction should remove the surface’s deviation from its own mean, not the surface itself. Remove the slope and keep the overall level.
A straight subtraction drives the background toward zero, and a black background is one of the clearest signs of over-processing. Real sky is never black. It is dark gray with faint structure emerging from it, and an image with a pure black background has usually lost its faintest real signal along with the gradient.
The nightscape exception
If your frame contains landscape, such as a Milky Way shot over a ridge or a foreground of trees or buildings, everything above needs qualifying.
The gradient model describes the sky. The ground is a different subject that happens to share the frame, and subtracting a sky gradient from a silhouetted ridge just darkens the ridge in a pattern that has nothing to do with it.
The ground also corrupts the fit. A dark silhouette across the bottom third reads as very dark sky and drags the surface down there, which then over-brightens the real sky above it.
So the horizon has to be found before the gradient is fitted, and brightness is the wrong way to find it. A moonlit hillside is brighter than the sky above it, and a silhouette is darker than any part of the sky. Brightness gets it backwards both ways.
Stars are the reliable test. Sky has point sources scattered through it at whatever density your lens and exposure reached. Foreground does not: a ridge, a tree or a building is a large connected region with no points in it, however bright or dark it is.
Telling a gradient from a cast
These two are confused all the time, and the fixes differ.
A gradient is spatial. One region is brighter than another. You fix it by fitting and subtracting a surface.
A cast is uniform. The whole frame is tinted because one channel sits above the others everywhere. You fix it with a per-channel shift, and it has to be a shift, not a scale. A shift moves the sky to a common level and leaves every difference above it as it was, so the nebula keeps its color while the sky loses its tint. Scaling drains the object along with the background, which is why “just turn down the red” gives you a gray, lifeless nebula.
Most light-polluted frames have both. Correct the cast first, then the gradient, then set the black point. Setting a level against a background that is about to move is wasted work.
Do it on linear data
All of this belongs before the nonlinear stretch.
A gradient is an additive property of linear data. After a stretch, that addition has been mapped through a curve, and subtracting a smooth surface no longer removes it cleanly. You would be fitting a distorted version of the problem.
In practice: calibrate, remove the gradient, correct the color, then stretch. If your workflow stretches first because that is when you can finally see what you are doing, the corrections will never quite land, and you will spend the rest of the session fighting residuals that a different order would have removed.
Checklist
- Linear data, before any stretch.
- Sky samples only. Exclude anything sitting on the object.
- Low order: linear, quadratic or cubic, no higher.
- Keep the level. Remove the slope and add the mean back.
- For nightscapes, find the horizon first, from where the stars are.
- Run it once. A second pass fits mostly noise and object and starts removing signal.
The last one catches people. If a gradient survives one properly fitted pass, another pass will not help. Either the surface is the wrong shape or your samples were contaminated, and running it again just takes a bite out of the target.
Try it on your own data
Akastroid runs its processing on your own frames and logs each step it took.
Download Akastroid