Hoppa till innehållet
AI-grafen
F· AI engineeringdatorseende· ca 90 min· volatil — kontrolleras ofta· verifierad 2026-09-21

Objektdetektion

Kunna förklara bounding boxes, IoU, NMS och använda en detektor.

Förkunskaper

Intuition

Klassificering svarar «vad finns i bilden?». Detektion svarar «vad finns var?» — en lista av rutor med klass och konfidens.

IoU (intersection over union) mäter hur väl två rutor överlappar:

IoU=snittareaunionsarea\mathrm{IoU} = \frac{\text{snittarea}}{\text{unionsarea}}

IoUBetyder
1,0identiska rutor
0,5tröskeln för «träff» i klassiska mått
0,0ingen överlappning

NMS (non-maximum suppression) löser att detektorn hittar samma objekt flera gånger: sortera rutorna efter konfidens, behåll den bästa, och kasta alla som överlappar den med mer än en tröskel. Upprepa.

Utan NMS får du tjugo rutor runt varje bil. Med NMS får du en.

Formellt

Två familjer:

Tvåstegs (Faster R-CNN)Enstegs (YOLO, RetinaNet, DETR)
Hurföreslå regioner, klassificera demförutsäg rutor direkt
Precisionhistoriskt högrenumera likvärdig
Hastighetlångsammarerealtid
Användsforskning, hög precisioni praktiken nästan alltid

DETR är en tredje väg: en transformer som förutsäger en fast mängd rutor och matchar dem mot facit med ungersk matchning. Ingen NMS behövs — modellen lär sig att inte duplicera.

mAP (mean average precision) är standardmåttet, och det missförstås ofta:

  1. För varje klass, sortera detektioner efter konfidens.
  2. Räkna precision och recall vid varje tröskel → precision-recall-kurva.
  3. AP är arean under den kurvan.
  4. mAP är medelvärdet över klasserna.
  5. mAP@[.5:.95] (COCO-standard) medelvärdesbildar dessutom över IoU-trösklarna 0,50 till 0,95.

Den sista punkten är viktig: [email protected] och mAP@[.5:.95] skiljer sig ofta med 15–20 procentenheter, och en jämförelse som blandar dem är meningslös.

Klassobalans är detektionens grundproblem: en bild har typiskt några objekt och tiotusentals bakgrundsregioner. Två lösningar:

LösningIdé
Focal lossnedvikta lätta exempel så att de svåra dominerar gradienten
Hard negative miningvälj ut de svåraste bakgrundsexemplen

Praktiska val:

BehovVal
Realtid på kantYOLO i liten variant
Hög precision, inte tidskritiskttvåstegs eller stor DETR-variant
Små objekthögre indataupplösning — viktigare än modellvalet
Få märkta bilderförtränad modell + kraftig augmentering

Raden om små objekt är den som oftast löser problemet i praktiken: att fördubbla upplösningen hjälper mer än att byta arkitektur.

Kod

import torch, torchvision
from torchvision.ops import box_iou, nms

# IoU för hand
def iou(a, b):
    """a, b: (x1, y1, x2, y2)"""
    x1, y1 = max(a[0], b[0]), max(a[1], b[1])
    x2, y2 = min(a[2], b[2]), min(a[3], b[3])
    snitt = max(0, x2 - x1) * max(0, y2 - y1)
    area_a = (a[2] - a[0]) * (a[3] - a[1])
    area_b = (b[2] - b[0]) * (b[3] - b[1])
    return snitt / (area_a + area_b - snitt)

print(round(iou((0, 0, 10, 10), (5, 5, 15, 15)), 4))     # 0.1429
print(round(iou((0, 0, 10, 10), (0, 0, 10, 10)), 4))     # 1.0
print(round(iou((0, 0, 10, 10), (20, 20, 30, 30)), 4))   # 0.0

# NMS: samma objekt hittat flera gånger → behåll den bästa
rutor = torch.tensor([[10., 10., 50., 50.], [12., 12., 52., 52.],
                      [11., 9., 49., 51.], [100., 100., 140., 140.]])
poang = torch.tensor([0.92, 0.88, 0.85, 0.79])
behall = nms(rutor, poang, iou_threshold=0.5)
print(behall.tolist())          # [0, 3] — tre överlappande blev en

# Använd en förtränad detektor
modell = torchvision.models.detection.fasterrcnn_resnet50_fpn(weights="DEFAULT").eval()
with torch.no_grad():
    ut = modell([bild])[0]
for ruta, etikett, p in zip(ut["boxes"], ut["labels"], ut["scores"]):
    if p > 0.7:
        print(f"  klass {int(etikett)}  konfidens {float(p):.2f}  {ruta.tolist()}")

# mAP: skilj på [email protected] och mAP@[.5:.95]
def ap_vid_troskel(pred_rutor, pred_poang, facit_rutor, iou_troskel=0.5):
    ordning = pred_poang.argsort(descending=True)
    pred_rutor = pred_rutor[ordning]
    matchad = torch.zeros(len(facit_rutor), dtype=torch.bool)
    tp = torch.zeros(len(pred_rutor))
    for i, p in enumerate(pred_rutor):
        if len(facit_rutor) == 0:
            break
        iou_rad = box_iou(p[None], facit_rutor)[0]
        iou_rad[matchad] = -1                        # redan använda facit kan inte matchas igen
        basta = int(iou_rad.argmax())
        if float(iou_rad[basta]) >= iou_troskel:
            tp[i] = 1.0
            matchad[basta] = True
    kum_tp = tp.cumsum(0)
    precision = kum_tp / torch.arange(1, len(tp) + 1)
    recall = kum_tp / max(len(facit_rutor), 1)
    # 101-punkts interpolerad AP, som COCO
    ap = 0.0
    for r in torch.linspace(0, 1, 101):
        p_max = precision[recall >= r].max() if (recall >= r).any() else torch.tensor(0.0)
        ap += float(p_max) / 101
    return ap

def map_coco(pred_rutor, pred_poang, facit_rutor):
    trosklar = torch.arange(0.5, 1.0, 0.05)
    return {
        "[email protected]": round(ap_vid_troskel(pred_rutor, pred_poang, facit_rutor, 0.5), 4),
        "mAP@[.5:.95]": round(float(sum(
            ap_vid_troskel(pred_rutor, pred_poang, facit_rutor, float(t))
            for t in trosklar) / len(trosklar)), 4),
    }

Skillnaden mellan de två talen i map_coco är ofta 15–20 procentenheter. Att jämföra en modells [email protected] med en annans mAP@[.5:.95] är ett av de vanligaste felen i detektionslitteraturen.

Behärskning innebär

  • Förklarar IoU och NMS
  • Tolkar mAP korrekt
  • Väljer detektorfamilj efter krav

Logga in för att göra övningarna och bygga upp din behärskning.

Källor

Alla källor och licenser