7.13. Suprimarea non-maximelor¶
O rețea de detectare produce de obicei mai multe casete candidate suprapuse în jurul aceluiași obiect din lumea reală: fiecare ancoră din apropierea obiectului se activează cu un scor similar, iar post-procesorul le vede pe toate. Suprimarea non-maximelor (NMS) este pasul care transformă acel grup într-o singură casetă.
Algoritmul este scurt: sortează casetele candidate după scor, ia caseta cu scorul cel mai mare, suprimă orice altă casetă care se suprapune cu ea peste un prag ales, apoi ia următoarea cu cel mai mare scor din ce a rămas și repetă. Metrica de suprapunere este intersecție-peste-uniune (IoU) – aria comună a două casete împărțită la aria lor combinată, o valoare între 0 (fără suprapunere) și 1 (casete identice). Argumentul de constructor nms_threshold de pe fiecare post-procesor livrat este pragul peste care casetele sunt tratate ca duplicate ale unei casete deja păstrate.
NMS comprimă un grup de detectări suprapuse la cea cu scorul cel mai mare.¶
7.13.1. Soft-NMS¶
Clasa livrată ml.utils.NMS implementează Soft-NMS, o rafinare care reduce scorul unei casete suprapuse cu o cantitate ce depinde de cât de mult se suprapune, în loc să elimine caseta direct. Dacă scorul redus scade sub prag, caseta este eliminată; altfel supraviețuiește cu scorul redus și concurează în runda următoare.
Parametrul nms_sigma controlează cât de agresivă este reducerea. Cu un nms_sigma mic (valoarea implicită livrată de 0.1) reducerea este abruptă: o casetă puternic suprapusă are scorul adus aproape de zero, iar Soft-NMS se reduce la NMS clasic. Cu un nms_sigma mai mare reducerea este blândă, iar casetele suprapuse ale unor obiecte diferite supraviețuiesc mai des, ceea ce contează când obiecte reale din aceeași clasă chiar se suprapun (o mulțime de fețe, un grup de palme).
Setarea lui nms_sigma la <= 0 dezactivează complet reducerea: casetele suprapuse trec mai departe cu scorurile lor originale, iar doar pragul de scor le filtrează.
7.13.2. Construirea directă a uneia¶
Fiecare post-procesor livrat construiește un NMS nou la fiecare inferență, adaugă fiecare candidat la el și apelează get_bounding_boxes() la final. Un post-procesor personalizat urmează același tipar:
from ml.utils import NMS
iw = model.input_shape[0][2]
ih = model.input_shape[0][1]
nms = NMS(iw, ih, inputs[0].roi)
for box, score, class_idx in candidates:
nms.add_bounding_box(box.xmin, box.ymin,
box.xmax, box.ymax,
score, class_idx)
result = nms.get_bounding_boxes(threshold=nms_threshold,
sigma=nms_sigma)
Constructorul preia lățimea și înălțimea de intrare a rețelei în pixeli și ROI-ul imaginii originale față de care a rulat modelul; add_bounding_box() preia coordonatele casetei în acel spațiu de pixeli al intrării rețelei, iar get_bounding_boxes() remapează supraviețuitorii înapoi în coordonatele imaginii folosind ROI-ul. Remaparea ține cont automat de întinderea de normalizare – același ROI pe care l-a văzut predictorul este folosit pentru a proiecta casetele înapoi – astfel încât casetele returnate sunt gata de desenat pe cadrul capturat.
Forma returnată este o listă de liste pe clase, indexată după label_index transmis lui add_bounding_box. Listele de clase goale sunt păstrate astfel încât indicele să corespundă indicelui de clasă al modelului; enumerate(result) parcurge clasele împreună cu detectările lor.