Ultralytics YOLO27:
Computer Vision Foundations·Pick a Task·Lesson 4/10
Lessonbeginner

Boxes vs Masks: When You Actually Need Segmentation

Measure what a mask adds over a box on a real image, and decide from the downstream consumer whether your project needs shape at all.

Masks are what most people picture when they imagine AI vision, and they're where teams burn the most labeling budget. A mask tells you an object's exact shape; a box only tells you where it is. That difference decides some products completely — and changes nothing for most of them.

Outcome

Measure the box-versus-mask gap on an image and choose boxes or masks for your project, justified by the code that reads the output.

Fast Track
If you already know your way around, here's the short version.
  1. Boxes give location; masks give shape, area, and exact edges.

  2. Area, cut-outs, or clearance? Masks. Counting, zones, tracking, or crops? Boxes.

  3. Per-object counts: instance segmentation. Only region coverage: semantic segmentation.

  4. Compare pixel counts at the same resolution: retina_masks=True.

  5. Time 20 boxes against 20 polygons before you commit the labeling budget.

Hands-on

What a mask adds#

Ultralytics YOLO instance segmentation of a city intersection, with a separate mask on each car, the bus, and every pedestrian

For each object, the Segment task returns everything a detector returns — class, box, confidence — plus the pixels that belong to the object, so each car above gets its own outline, separate from the bus and the people. Read the mask as a polygon (masks.xy) or as a binary image (masks.data).

How much does shape change the numbers? Compare each object's box area with its mask area:

from ultralytics import YOLO

model = YOLO("yolo26n-seg.pt")
result = model("https://ultralytics.com/images/bus.jpg", retina_masks=True, verbose=False)[0]

for box, mask in zip(result.boxes, result.masks.data):
    x1, y1, x2, y2 = box.xyxy[0].tolist()
    box_area = (x2 - x1) * (y2 - y1)
    mask_area = float(mask.sum())  # pixels inside the object's mask
    name, ratio = result.names[int(box.cls)], mask_area / box_area
    print(f"{name:7} box={box_area:7.0f} px  mask={mask_area:7.0f} px  ({ratio:.0%})")
person  box=  97116 px  mask=  49331 px  (51%)
person  box=  68102 px  mask=  21316 px  (31%)
bus     box= 410093 px  mask= 255265 px  (62%)
person  box=  55998 px  mask=  33524 px  (60%)
person  box=  23690 px  mask=  11109 px  (47%)

The bus fills only 62% of its box, and one person fills 31%. Any number computed from boxes alone — painted area, spill size, how much of a shelf is empty — would be off by that margin. If your product reports a number like that, you need masks. If it only counts or locates, the extra pixels change nothing.

Compare pixel counts at the same resolution

Symptom: mask areas that look 3–4× too small next to box areas. Cause: by default masks.data comes back at the inference size (here 640×480), while boxes are in original-image pixels (1080×810). Fix: pass retina_masks=True, as above, or scale the counts yourself.

Decide from the consumer, not the demo#

Downstream needBoxes enough?Why
Count objects or check presenceYesLocation is the whole answer
Alert when an object enters a zoneYesA box crossing a line is enough
Track objects across framesYesTrackers associate boxes
Crop for a second classifierYesA crop only needs the box
Measure area or coverage (weeds, rust, spills)NoIrregular shapes make box area meaningless
Cut the object out (background removal, AR)NoYou need the exact edge
Check clearance or contact (robot grasp, collision)NoThe box overstates the object
Separate touching, irregular objectsOften noOverlapping boxes blur which pixels belong to whom

If every row your product needs says "yes", ship a detector. Reach for YOLO26 segmentation only when shape earns its keep.

Instance vs semantic segmentation#

Instance segmentation gives every object its own mask, so three overlapping cars stay three cars. Semantic segmentation labels each pixel with a class and nothing else: all car pixels become one "car" region, and the count is gone. Count or measure individual objects — cells, fruit, defects — with instance segmentation. Measure region coverage — road versus sidewalk, crop versus soil — with semantic segmentation.

What shape costs to label#

A box takes two clicks. A good polygon can take dozens, and every edge is a place where annotators can disagree: does the bag belong to the person, is the shadow part of the car? Measure your own cost instead of trusting a rule of thumb: time an annotator on 20 objects as boxes and the same 20 as polygons, then multiply the difference by your dataset size.

AI-assisted tools close much of the gap. Smart Annotation and Batch Labeling proposes a mask from a click or a box, so annotators correct edges instead of tracing them — but someone still reviews every boundary.

Write the boundary rules before the first mask

Decide occlusion, transparent objects, shadows, objects cut off by the frame edge, and attached items (bags, handles, loads) before labeling starts, and give the same page to annotators and reviewers. Annotation Best Practices covers how to enforce them; the COCO-Seg dataset shows consistent masks at scale.

When you spot-check masks, look for bleed (mask spills past the edge), holes (missed regions inside the object), phantom edges (mask follows a JPEG artifact), and ambiguous parts (is the suitcase handle in the suitcase mask?).

Try It

Run the box-versus-mask script on an image from your own project (or bus.jpg). Record the mask-to-box ratio for your main class, find the row in the consumer table that matches your downstream need, and write one line: “boxes” or “masks”, followed by the consumer that decided it.

Done When
You've finished the lesson when all of these are true.
  • You have a measured mask-to-box ratio for your main class.

  • You have one written line choosing boxes or masks, naming the downstream consumer that decided it.

  • If you chose masks, you have timed 20 boxes against 20 polygons and listed your boundary rules.

Show solution

Suppose two products share the bus camera: a people counter and a street-occupancy report that says how much of the scene vehicles cover. Measure both answers from the same run:

from ultralytics import YOLO

model = YOLO("yolo26n-seg.pt")
result = model("https://ultralytics.com/images/bus.jpg", retina_masks=True, verbose=False)[0]
height, width = result.orig_shape

for name in sorted({result.names[int(c)] for c in result.boxes.cls}):
    rows = [i for i, c in enumerate(result.boxes.cls) if result.names[int(c)] == name]
    union = result.masks.data[rows].amax(dim=0)  # merge masks so overlaps count once
    box_union = sum(float((b[2] - b[0]) * (b[3] - b[1])) for b in result.boxes.xyxy[rows])
    print(f"{name:6} {len(rows)} objects | masks cover {float(union.sum()) / (height * width):.0%} of the image, boxes {box_union / (height * width):.0%}")
bus    1 objects | masks cover 29% of the image, boxes 47%
person 4 objects | masks cover 13% of the image, boxes 28%
  • People counter → boxes. The count is 4 either way; masks add labeling cost and change nothing.
  • Occupancy report → masks. Boxes claim the bus covers 47% of the scene; it really covers 29%. A box-based report would be wrong by more than half.
What's next

Next: two specialized tasks — pose, where the output is a skeleton, and OBB, where the box rotates.