5.30. Template matching

De detectoren die tot nu toe zijn behandeld, beantwoorden vragen over de inhoud van een enkel frame: waar de blobs zitten, waar de lijnen lopen, wat een gedrukte code zegt. Een ander soort vraag vergelijkt de ene afbeelding met een andere. Lijkt dit gebied van het vastgelegde frame op de referentiepatch die ik bij de kalibratie heb opgeslagen? De matching-methoden beantwoorden die vraag.

Tonale en statistische analyse introduceerde get_similarity() voor de verwante vraag – hoe gelijk zijn deze twee even grote afbeeldingen in hun geheel? – met SSIM als de onderliggende maatstaf. De resterende matching-vraag is die van de lokalisatie: niet “hoe gelijk zijn deze twee afbeeldingen” maar “waar binnen deze grotere afbeelding verschijnt die kleinere patch?” Het juiste gereedschap voor de lokalisatievraag is template matching.

5.30.1. De basisaanroep

find_template() zoekt naar de eerste plaats waar een kleine template-afbeelding voorkomt in het vastgelegde frame. De implementatie gebruikt genormaliseerde kruiscorrelatie (NCC): de template schuift over het frame, de matchscore per positie wordt berekend uit de correlatie tussen de templatepixels en de onderliggende framepixels (genormaliseerd tegen de lokale gemiddelden en varianties zodat versterkingswijzigingen de match niet misleiden), en de eerste positie waarvan de score threshold overschrijdt wordt als begrenzingsvak teruggegeven:

template = image.Image("/sdcard/template.bmp", copy_to_fb=False)
template.to_grayscale()

match = img.find_template(template, threshold=0.7,
                           search=image.SEARCH_DS)

if match is not None:
    img.draw_rectangle(match, color=(255, 0, 0))

De methode werkt alleen op grijswaardenafbeeldingen. Leg vast in grijswaarden (de natuurlijke keuze voor elke cam zonder kleursensor), of converteer ter plaatse via to_grayscale() vóór de aanroep. Hetzelfde geldt voor de van schijf geladen template: een kleurentemplate wordt met dezelfde methode geconverteerd, en het resultaat is wat de matcher verwacht.

threshold is een float van 0.0 tot 1.0. Een waarde van 1.0 vereist een perfecte pixel-voor-pixel-match (wat bij echte vastgelegde afbeeldingen nooit gebeurt), 0.0 accepteert alles, en waarden tussen 0.6 en 0.8 dekken het gangbare geval waarbij de template onder vergelijkbare belichting is vastgelegd en de scène niet ingrijpend is veranderd. Verhoog de drempelwaarde om valse positieven te onderdrukken; verlaag deze om ruisigere matches te accepteren ten koste van meer onterechte treffers.

5.30.2. Zoekstrategie

search kiest tussen twee strategieën. image.SEARCH_EX is de uitputtende zoekopdracht: de template schuift door elke step-pixelpositie in het frame en geeft de eerste treffer boven de drempelwaarde terug. image.SEARCH_DS is de diamantzoekopdracht: de matcher bemonstert eerst grof en verfijnt vervolgens rond de beste score, wat dramatisch sneller is maar een echte match kan missen als de grove doorgang toevallig nabij een lokaal maximum belandde dat het globale verslaat. Voor een realtime-pijplijn waar de template goed gedefinieerd is en niet snel verward zal raken, is SEARCH_DS de juiste standaard; voor een eenmalige kalibratie waarbij de kosten van een misser hoger zijn dan die van een tragere scan, is SEARCH_EX veiliger.

step bepaalt de pixeloverslag tijdens de uitputtende doorgang (de diamantzoekopdracht beheert zijn eigen stap). Grotere step-waarden versnellen de scan ten koste van subpixelnauwkeurigheid. roi beperkt de zoekopdracht tot een gebied van het frame, wat zowel beperkt wat de matcher in overweging neemt als het werk vermindert.

De teruggegeven waarde is een (x, y, w, h)-begrenzingsvaktupel dat de beste match identificeert, of None als geen enkele positie de drempelwaarde overschreed. Het begrenzingsvak past rechtstreeks in draw_rectangle() of crop() voor de volgende verwerkingsfase.

5.30.3. De val van schaal en rotatie

De klassieke valkuil bij template matching is gevoeligheid voor schaal en rotatie. De matcher vergelijkt de template pixel-voor-pixel met het frame; een template die op de ene afstand is vastgelegd matcht niet hetzelfde object dat op een andere afstand is vastgelegd, en een recht-van-voren vastgelegde template matcht niet hetzelfde object dat van opzij wordt bekeken. De drempelwaarde zakt stilletjes onder het matchniveau, zelfs wanneer het object voor het menselijk oog duidelijk zichtbaar is, en de methode geeft None terug.

Voor de eenvoudige gevallen bestaan er enkele tijdelijke oplossingen. De applicatie kan meerdere templates op verschillende schalen vastleggen en find_template() voor elk achtereenvolgens uitvoeren, waarbij de eerste die de drempelwaarde overschrijdt wordt geaccepteerd; de kosten schalen mee met het aantal templates. De applicatie kan het frame voorbewerken met rotation_corr() of de polaire transformatie (Geometrische transformaties) om de hinderlijke rotatie te verwijderen voordat de match wordt uitgevoerd; de gematchte template moet nog steeds overeenkomen met de gecorrigeerde geometrie.

Een nuttig idioom voor QA-inspectiepijplijnen koppelt de template-matcher aan de overeenkomstscorer die in Tonale en statistische analyse werd geïntroduceerd: find_template() lokaliseert het onderdeel in het vastgelegde frame en het teruggegeven begrenzingsvak wordt uitgesneden en doorgegeven aan get_similarity() tegen de referentiepatch. De template-match-stap bepaalt waar het onderdeel is; de overeenkomstscore-stap bepaalt of het onderdeel acceptabel is. De twee stappen draaien elk frame, de drempelwaarde op mean is de slaag/zak-poort, en het gematchte begrenzingsvak dat terug in het frame wordt getekend is de IDE-preview die de operator bekijkt.