7.13. Non-max suppression¶
Een detectienetwerk produceert doorgaans meerdere overlappende kandidaatvakken rond hetzelfde object uit de echte wereld: elk anker in de buurt van het object reageert met een vergelijkbare score, en de post-processor ziet ze allemaal. Non-max suppression (NMS) is de stap die dat cluster omzet in één vak.
Het algoritme is kort: sorteer de kandidaatvakken op score, neem het vak met de hoogste score, onderdruk elk ander vak dat er met een gekozen drempelwaarde mee overlapt, neem vervolgens het hoogste van wat overblijft, en herhaal dit. De overlapmaat is intersection-over-union (IoU) – de gedeelde oppervlakte van twee vakken gedeeld door hun gecombineerde oppervlakte, een waarde tussen 0 (geen overlap) en 1 (identieke vakken). Het nms_threshold constructorargument op elke meegeleverde post-processor is de grens waarboven vakken worden behandeld als duplicaten van een al behouden vak.
NMS reduceert een cluster van overlappende detecties tot het vak met de hoogste score.¶
7.13.1. Soft-NMS¶
De meegeleverde ml.utils.NMS klasse implementeert Soft-NMS, een verfijning die de score van een overlappend vak verlaagt met een hoeveelheid die afhangt van hoeveel het overlapt, in plaats van het vak meteen weg te gooien. Als de verlaagde score onder de drempelwaarde komt, wordt het vak weggegooid; anders blijft het behouden met de verlaagde score en doet het mee in de volgende ronde.
De nms_sigma parameter bepaalt hoe agressief de verlaging is. Met een kleine nms_sigma (de meegeleverde standaardwaarde van 0.1) is de verlaging steil: een sterk overlappend vak krijgt zijn score naar bijna nul gedreven en Soft-NMS herleidt zich tot klassieke NMS. Met een grotere nms_sigma is de verlaging zachter en blijven overlappende vakken van verschillende objecten vaker behouden, wat van belang is wanneer objecten van dezelfde klasse uit de echte wereld werkelijk overlappen (een menigte gezichten, een cluster handpalmen).
Het instellen van nms_sigma op <= 0 schakelt de verlaging volledig uit: overlappende vakken passeren met hun oorspronkelijke scores, en alleen de score-drempelwaarde filtert ze.
7.13.2. Er rechtstreeks een bouwen¶
Elke meegeleverde post-processor bouwt per inferentie een nieuwe NMS, voegt elke kandidaat eraan toe, en roept aan het einde get_bounding_boxes() aan. Een aangepaste post-processor volgt hetzelfde patroon:
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)
De constructor neemt de invoerbreedte en -hoogte van het netwerk in pixels en de ROI van de oorspronkelijke afbeelding waartegen het model draaide; add_bounding_box() neemt vakcoördinaten in die pixelruimte van de netwerkinvoer, en get_bounding_boxes() zet de overgeblevenen terug naar afbeeldingscoördinaten met behulp van de ROI. De terugzetting houdt automatisch rekening met de normalisatie-rek – dezelfde ROI die de voorspeller zag wordt gebruikt om vakken terug te projecteren – zodat de geretourneerde vakken klaar zijn om op het vastgelegde frame te tekenen.
De geretourneerde vorm is een lijst van lijsten per klasse, geïndexeerd op de label_index die aan add_bounding_box is doorgegeven. Lege klasselijsten blijven behouden zodat de index overeenkomt met de klasse-index van het model; enumerate(result) doorloopt de klassen samen met hun detecties.