2026-09-08
Why your cutout has a faint outline of the background you removed
You cut the subject out. The preview looks clean against the grey-and-white checkerboard, so you download the PNG. Then you drop it onto a dark poster, a navy slide, or a saturated brand colour — and there it is: a faint pale outline tracing the entire silhouette, as if the subject were lifted off the page with a thin glow still stuck to it.
That rim is not a rendering glitch and it is not random. It is the background you thought you removed, still sitting in the file, at reduced opacity. Every automatic cutout produces some version of it; NearBG's version has a width you can calculate in advance. This is a post about which pixels carry the rim, why it is the size it is, what genuinely removes it, and which of those fixes NearBG does not perform for you.
First: the checkerboard preview is lying to you
The transparency checkerboard every image tool draws behind a cutout is light grey on white. Most photographs are taken against something in roughly that brightness range — a wall, a window, a tabletop, an overcast sky. So a rim made of leftover light background pixels sits on a light checkerboard and disappears completely. It only becomes visible at the moment you place the cutout on the background you actually wanted, which is usually darker or more saturated than the one you shot against.
The practical consequence comes before any of the technical detail below: judge a cutout against the colour it is going to live on, not against the checkerboard. Paste it into the poster, the slide, or the product page first. If the rim is there, it was there the whole time.
A half-transparent pixel still stores a full colour
PNG stores alpha unassociated. The W3C's PNG rationale is explicit about why: PNG uses "unassociated" or "non-premultiplied" alpha so that images with separate transparency masks can be stored losslessly, because with premultiplied alpha "any image data hidden by the transparency mask is irretrievably lost by that method, since multiplying by a zero alpha value always produces zero" (PNG Specification: Rationale, §12.8).
Read that as a plain statement about your cutout file: a pixel with alpha 0.3 does not store a faded colour. It stores its full, original R, G and B values next to the number 0.3, and the multiplication only happens later, when something composites it onto a background. So the whole question of whether you get a halo reduces to one thing — what colour is written into the partly-transparent pixels around your subject?
Background removal does not change a single colour value
NearBG's answer, and most tools' answer, is: exactly the colour the camera recorded there,
untouched. The cutout step draws the full-colour photo onto a canvas and then applies the mask with
the Canvas 2D destination-in operator, which the W3C defines as
co = ab x Cb x as, ao = ab x as
(Compositing and
Blending Level 1, §9.1.7). Undo the premultiplication and the surviving colour is
Cb — the backdrop's own colour, unchanged. Only the alpha channel moves.
This is worth stating flatly because it contradicts the mental model most people have. "Removing the background" never removes a colour from anything. It writes a number between 0 and 1 onto every pixel and leaves the photograph itself completely intact underneath. A pixel that was 100% wall before the cutout is still 100% wall afterwards. If the mask gave it alpha 0.3 instead of 0, that wall pixel ships in your PNG at 30% opacity.
Why NearBG's rim is roughly one three-hundred-twentieth of the photo
So the width of the halo is the width of the band where the mask lands between 0 and 1. In NearBG that band has a floor set by the architecture, not by the photo.
The model NearBG runs, U2Netp, has a fixed 320×320 input. Your photo is scaled down to fit
inside that square, the model returns a 320×320 saliency map, and that map is then scaled
back up to the photo's real dimensions and used as the alpha channel. Scaling up on a
canvas smooths by default: imageSmoothingEnabled "must be set to true" when the context
is created, and when scaling up with it on, the user agent "should attempt to apply a smoothing
algorithm to the image data"
(HTML Standard,
§4.12.5.1.18 Image smoothing). Bilinear-style interpolation is exactly what you want when
enlarging a photo. Applied to a mask, it turns a hard yes/no boundary into a gradient.
One pixel of the 320-wide mask therefore becomes this many pixels of your photo:
| Photo's long side | Upscale factor | One mask pixel becomes |
|---|---|---|
| 1,280 px | 4.0× | about 4 px |
| 1,920 px | 6.0× | about 6 px |
| 3,024 px | 9.5× | about 9–10 px |
| 4,032 px (a 12 MP phone photo) | 12.6× | about 12–13 px |
| 6,000 px (a 24 MP camera file) | 18.8× | about 18–19 px |
Note what that table says about resolution. Shooting at higher resolution makes the halo wider in pixels, not narrower, because the mask stays 320 wide no matter what you feed it. A 24 MP file gets a rim roughly four and a half times wider in pixels than a 1,280 px web image of the same scene.
The arithmetic of the halo, with real numbers
Take a pixel that sits four or five real pixels outside the true silhouette. It is 100% wall: say a bright indoor wall at RGB (235, 238, 240). The true mask value there should be 0. After the upscale, the smoothed ramp gives it something like 0.30 instead.
Composite that onto a dark poster at RGB (20, 24, 32). Normal source-over compositing
is co = as x Cs + ab x Cb x (1 - as)
(Compositing
and Blending Level 1, §9.1.4), so:
0.30 x (235,238,240) + 0.70 x (20,24,32) = (85, 78, 94)
Against a backdrop of (20, 24, 32), a pixel at (85, 78, 94) is roughly four times brighter. It should have been the poster colour and nothing else. Multiply that by a 13 px band running the full length of the silhouette and you have a halo, not a stray pixel.
Notice what this is not. This is not the hair-and-fur problem, where a pixel is genuinely part subject and part background and a segmentation model has to guess (covered separately on this blog). Those pixels really are ambiguous. The halo described here happens on a clean, high-contrast edge — a shoulder against a plain wall — where the model got the boundary right and the resampling step smeared it afterwards. It affects perfectly easy photos.
The rule of thumb this gives you
One mask pixel is always 1/320 of the photo's long side, which is 0.31%. So the halo is the same proportion of the image no matter what resolution you shot at — and that makes the only number that matters the size the cutout is finally displayed or printed at.
- Placed 400 px wide on a web page: about 1.3 px. Effectively invisible.
- Placed 1,200 px wide in a slide: about 3.8 px. Noticeable on a dark slide.
- Printed 40 cm wide on a poster: about 1.25 mm. Clearly visible from arm's length.
Which means the cheapest fix in the whole article is: if the cutout is going to be small in the final layout, scale it down and stop worrying. The halo scales down with it. If the cutout is going to be large, or printed, you have to deal with it properly.
What actually removes it
Ranked roughly by how much work they cost, and honest about which ones NearBG performs.
- Shoot against a background close in brightness to the destination. Free, and the most effective thing on the list. The halo is only visible because the leftover pixels contrast with the new background. A subject shot against mid-grey and placed on a mid-grey poster has a halo too — you just cannot see it. If you know the cutout is headed for a dark layout, shoot against something dark.
- Scale the cutout down in the final layout, per the arithmetic above.
- Give the cutout a deliberate outline. A 3–6 px stroke in your design tool, sticker-style, covers the ramp entirely and reads as a design choice rather than a repair. This is why die-cut-sticker layouts almost never show halos.
- Flatten the semi-transparent pixels against the destination colour before you place them. GIMP ships this as Semi-Flatten (Filters → Web → Semi-Flatten): the "process will combine FG color to layer (logo) color, proportionally to corresponding alpha values, and will rebuild correct anti-aliasing" (GIMP 2.10 documentation, §15.3). Set the foreground colour to your poster's exact colour first. It only works when the destination is a single flat colour, but for menu boards, brand-colour posters and slides, that is precisely the case.
- Contract the alpha edge inward, or run a defringe/matte-removal command. Desktop editors have these under various names; they either shave the ramp off or repaint the ramp's colour from pixels further inside the subject. Both trade a little of the subject's outline for a clean edge.
- Erase the ramp by hand in NearBG. The touch-up brush works, with the caveats in the next section.
What NearBG does and does not do about this
Everything below is what the shipped tool actually does, not what a background remover in general might do.
- The touch-up brush only subtracts. It punches alpha to zero under the cursor
using
destination-out, which the same W3C spec defines asco = ab x Cb x (1 - as)(§9.1.9). You can shave the ramp off. You cannot add alpha back, soften an edge, or recover detail the mask never captured. - The brush is coarse relative to the ramp. Its radius runs from 8 to 100 px, measured in the photo's own pixels, not screen pixels. On a 4,032 px photo the smallest brush is a 16 px-wide dab against a 13 px ramp — the right order of magnitude, but it will take real subject pixels with it, and it leaves a hard edge where a soft one used to be. Zoom in before you use it.
- Reset is all-or-nothing. There is no per-stroke undo. The reset button restores the untouched automatic result and discards every manual edit you made after it.
- There is no background replacement. NearBG hands you a transparent PNG and stops. That is a deliberate scope decision, but it has a direct cost here: you cannot preview the cutout against your destination colour inside the tool, which is the only place the halo is visible. You have to check in whatever tool you are composing in.
- There is no defringe, no decontaminate, no matte colour, and no edge feather. None of the remedies in the previous section are implemented. They are all things you do afterwards, elsewhere.
- One photo at a time, no resizing, PNG out. No batch. No output-size control — the PNG comes back at your source resolution, so the 13 px ramp comes back with it. Input is PNG, JPEG or WebP, up to 500 MB on a desktop or 50 MB on a phone or a low-memory device.
Where the halo is the least of your problems
The same 320×320 arithmetic sets a hard floor on what the mask can see at all. Anything narrower than about one mask pixel — 13 px on a 12 MP phone photo, 19 px on a 24 MP file — has no reliable representation in the mask. A skewer, a wire, an antenna, a whisker, a wisp of steam, a thin plant stem: these do not come out rough, they come out gone, or smeared into a soft grey suggestion of themselves. No amount of edge cleanup brings them back, because they were never in the mask.
Glass and other transparent objects fail in a different and more fundamental way. A saliency model answers "is this pixel part of the salient object", which for a glass tumbler has no correct binary answer: the pixel is glass and whatever is behind the glass. In practice you get one of two wrong answers — the tumbler is kept whole, with the original background frozen inside it, or it is dropped. Neither is a cutout of a glass. If your photo's subject is glass, a transparent bottle, a veil or an ice cube, this tool is not going to produce the result you want, and no touch-up pass will fix it.
Low-contrast boundaries are the third case, and they produce a broader version of the halo rather than a crisp one. The mask value NearBG uses is the model's raw saliency output, clamped to 0–1 and used directly as alpha. Where the model is genuinely uncertain — a dark jacket against a dark wall — it returns middling values across a wide region, and those become a broad, ghostly semi-transparency instead of a 13 px band.
Honest summary
A halo around a cutout is not a sign that anything failed. It is the arithmetic of an alpha mask computed small and enlarged, plus the fact that PNG keeps the colours of partly-transparent pixels exactly as photographed. In NearBG that band is about 1/320 of your photo's long side, which is about 13 px on a phone photo and about 0.31% of the image at any resolution — so the question is never "is there a halo" but "is it big enough to see at the size I am using this at."
Sources, so you can check them: the non-premultiplied alpha rule and the reasoning behind it come
from the W3C's
PNG Specification:
Rationale, §12.8; the destination-in, destination-out and
source-over formulas come from
Compositing and Blending Level 1, sections
9.1.7,
9.1.9 and
9.1.4;
the default-on image smoothing that widens the ramp is specified in the
HTML Standard,
§4.12.5.1.18; and the Semi-Flatten description is from the
GIMP 2.10 documentation.
Everything said about NearBG itself — the 320×320 fixed input, the upscale, the
erase-only brush at 8–100 px, the single all-or-nothing reset, no background replacement,
no defringe, no batch, no resizing — comes from the tool's own source, and the pixel widths in
the table are just division you can redo yourself.