mytool.cool

Removing a solid background without destroying the shadow

6 min read

On this page

Cut a product shot out of a flat background and the first casualty is the shadow.

It is not a bug in any particular tool. A drop shadow is, physically, the background colour multiplied down. To a classifier asking "is this pixel background?", the answer is yes — it is the same hue, the same saturation, just darker. So it gets removed with everything else, and the product ends up floating.

The cutout is technically correct and practically useless.

A shadow is not an object

Put a black overlay of opacity α over a background of luminance L_bg. The composite is:

L = (1 − α) · L_bg

Solve for alpha:

α = 1 − L / L_bg

That is the entire model. A shadow pixel's true alpha is a function of how much darker it is than the background it falls on — nothing else. No training data, no segmentation, no guess about what the subject is.

The reason this works is the same reason it fails elsewhere: the shadow lives on the background-to-black axis. Its chroma direction is unchanged; only luminance drops. A dark object also has low luminance, but its colour usually moves in a different direction. That difference is what lets you separate "darker background" from "dark thing".

Where the model actually holds

We measured it against synthetically generated shadows with known ground truth. The error figures below are mean absolute alpha error inside the shadow band, across five background colours — the same geometry each time, only the background changes:

Background Saturation Luminance Classifier runs Edge band left opaque Shadow alpha error
#24FA11 green 0.93 159 yes 0% 0.031
#00FF00 pure green 1.00 150 yes 0% 0.030
#0047BB cinema blue 1.00 63 yes 0% 0.048
#0000FF pure blue 1.00 29 no 100% 0.110
#FFFFFF white 0.00 255 no 100% 0.113

Two of those rows are failures, and they fail for different reasons.

Failure one: dark saturated backgrounds

#0000FF is not a hard colour because it is blue. It is hard because its luminance is 29. At that level the shadow's darkening is compressed into a narrow band of quantised values — a shadow that should read α = 0.05 and one that should read α = 0.15 land two 8-bit steps apart. The signal-to-noise ratio of the whole model collapses.

The measurement says exactly that: 0.110 error, 3.7× worse than green, with the entire anti-aliased edge band surviving at full opacity because the classifier that removes it never ran.

#0047BB is the control case. Same hue family, luminance 63, error 0.048 — better than the pure green. So the variable is not colour, it is how much room the shadow has to move.

Failure two: white and grey

White has saturation zero. There is no chroma direction to compare against, so the "is this on the background axis" test has nothing to work with — the whole ring-classification stage skips, and the edge band survives untouched.

This is not a bug we plan to fix by lowering a threshold. On a neutral background, a shadow and a grey object are genuinely ambiguous from colour alone: both lie on the same axis, and only spatial reasoning (the shadow is attached to the object's base, has a soft gradient, and doesn't have its own outline) can separate them.

So the limitation is stated up front on the tool page: shadow preservation works best on saturated colours. On white and grey the pipeline is deliberately conservative — it removes the background, and treats the shadow as part of it.

Why one threshold cannot do both jobs

Our first implementation had a single gate: if background luminance was below 40, skip the whole edge-classification stage. The intent was "shadows are unreliable on dark backgrounds, don't guess".

The effect was broader than the intent. That stage does two independent things:

  • removing the anti-aliased ring of background colour around the subject (needs only relative comparisons — "darker than background" — and works fine at any luminance above 1);
  • estimating shadow alpha (needs absolute luminance headroom, and genuinely fails below ~40).

One threshold, two responsibilities, and the weaker one vetoed the stronger one. On #0000FF that meant the edge band was left at 100% opacity — a hard, green-blue rim — because the shadow maths next to it was unreliable.

The fix was structural: split the gate. Keep the ring removal on relative tests, restrict the luminance gate to the shadow branch only. Same numbers, one less category of failure.

A dark object is not a shadow

The inverse mistake is just as easy. A dark object that happens to sit near the background hue will look like a shadow to a purely directional test.

We hit this with a glass sphere. Its dark green rim had a cosine similarity of 0.997 with the screen colour — essentially the same hue, just darker. The classifier called it shadow, assigned α = 1 − L/L_bg ≈ 0.42, and the unmixing step washed it to neutral grey. A green glass wall became a grey ring on white.

A single cue was never going to be enough. The classifier needs at least three: colour direction, band thickness, and position relative to the object. A 20-pixel-thick rim is not a shadow; a shadow is thin where it meets the object and fades outward.

What "keeping the shadow" should look like

A preserved shadow is not a painted-on effect. It should be real per-pixel alpha, which means:

  • it composites correctly on any new background, not just the one you previewed on;
  • the contact edge stays dark and tight, while the far edge fades smoothly;
  • it never has a rectangular cut where the background removal stopped.

If a tool gives you a shadow that looks right on white but grey on a dark background, the shadow was flattened into colour, not converted into alpha.

Three checks you can run

  1. Composite on white, then on black. A real alpha shadow keeps its shape and just changes contrast. A fake one turns into a grey smear on one of the two.
  2. Look at the contact line. Where the object meets the shadow, alpha should be near 1 and fall off outward. A uniform band means the shadow was pasted, not measured.
  3. Use a saturated background. If shadow preservation works on green but not on white, that is the expected limitation of a luminance model — and it is better to know which regime you are in than to discover it in a listing photo.

If you want to see it on your own product shot, the remover keeps the shadow and runs entirely in your browser.

Try it: Keep the shadowCut the background, keep the drop shadow.

Related reading