Walsh Template Matching mit Rotation

In diesem Beispiel wird ein Template Matching mit Berücksichtigung der Drehlage durchgeführt, um alle Instanzen eines Referenzbildes in einem Suchbild zu lokalisieren und deren Orientierung zu bestimmen.

Dazu wird eine die folgenden Funktionen verwendet:

Erläuterung

Beim Walsh Template Matching werden Instanzen eines Referenzbildes im Eingangsbild auf Basis der Walsh-Transformation gesucht. Da für einen zuverlässigen Vergleich meist schon wenige dieser Projektionen ausreichen, ist das Verfahren sehr performant und ermöglicht so auch die Suche über verschiedene Rotationswinkel, um Position und Orientierung gedrehter Objekte zu bestimmen.

Das Sample enthält drei verschiedene Templatebilder zum Testen. Das zu verwendende Template wird über sFolderPath und sTemplateFileName festgelegt. Um ein neues Template zu laden, muss bTemplateLoaded auf FALSE gesetzt werden, woraufhin das Bild automatisch neu eingelesen wird. Die Matching-Parameter sollten nach einem Templatewechsel überprüft und gegebenenfalls angepasst werden.

Über den Parameter bUseRotationSteps kann zwischen den beiden Funktionen umgeschaltet werden:

Die Parameter nRotations bzw. nAngles definieren die Anzahl der zu prüfenden Winkelschritte und bestimmen somit die Winkelauflösung der Suche. Da der Algorithmus zwischen diesen diskreten Prüfschritten interpoliert, können auch Rotationswerte zwischen den expliziten Prüfwinkeln ermittelt werden. Die gewählte Anzahl der Prüfrotationen hat einen maßgeblichen Einfluss auf die Gesamtlaufzeit der Funktion. Zur Performance-Optimierung ist es daher empfehlenswert, den Suchbereich mittels F_VN_FindTemplateWalshExp_AngleRange auf den tatsächlich relevanten Winkelbereich einzugrenzen. Dadurch lässt sich die Anzahl der Prüfwinkel reduzieren und die Ausführungszeit gezielt minimieren.

Darüber hinaus wird die Laufzeit und die erreichbare Matching-Qualität maßgeblich durch das Zusammenspiel von nProjections und fScaleFactor bestimmt. Eine höhere Anzahl an Projektionen lässt mehr Bildinformationen in den Prozess einfließen, was die Zuverlässigkeit des Matchings verbessert, jedoch die Rechenzeit erhöht. Ebenso steigert ein größerer Skalierungsfaktor (näher an 1.0) die Genauigkeit bei gleichzeitig längerer Rechenzeit. Da beide Parameter die Ähnlichkeitswerte direkt beeinflussen, muss der Schwellwert fMatchThreshold nach jeder Parameteränderung entsprechend neu abgestimmt werden.

Am Ende dieses Kapitels finden Sie weitere Hinweise zur Parametrierung und Optimierung der hier beschriebenen Parameter.

Zusätzlich wird eine ROI (stMatchingRoi) gesetzt, die den Suchbereich auf einen relevanten Bildausschnitt einschränkt. Dies reduziert die Anzahl der auszuwertenden Pixel und verkürzt somit die Rechenzeit. Es können jedoch nur Objekte gefunden werden, die vollständig innerhalb der ROI liegen.

Ausführung des Template Matchings

Zunächst wird die ROI im Eingangsbild definiert. Abhängig vom Parameter bUseRotationSteps wird anschließend entweder die 360°-Suche (F_VN_FindTemplateWalshExp) oder die gezielte Suche in einem definierten Winkelbereich (F_VN_FindTemplateWalshExp_AngleRange) ausgeführt.

Da der Parameter nRotations auf die festen Werte 1, 2, 4, 8, 16 und 32 beschränkt ist, stellt eine vorgeschaltete CASE-Anweisung sicher, dass die Eingabe auf den nächstkleineren gültigen Wert abgerundet wird. Ohne diese Korrektur würde die Funktion einen Fehlercode HRESULT 0x70B (INVALIDPARM) zurückgeben.

Für beide Suchfunktionen werden dem Parameter eOptions die Flags TCVN_FTWO_FUSE_MATCHES und TCVN_FTWO_ROTATED_RECTANGLE übergeben. Die Verwendung von TCVN_FTWO_FUSE_MATCHES ist grundsätzlich immer zu empfehlen, da diese Option redundante Treffer aus leicht abweichenden Rotationen und Positionen zu einem einzelnen Match zusammenfasst. Das Flag TCVN_FTWO_ROTATED_RECTANGLE vereinfacht die anschließende Auswertung deutlich, da die Ergebnisse direkt als strukturierter Datentyp TcVnRotatedRectangle zur Verfügung gestellt werden.

Variablen

// ROI for the template matching
stMatchingRoi       :   TcVnRectangle_UDINT := (nX := 150, nY := 150, nWidth := 740, nHeight := 650);

// Template matching parameters
bUseRotationSteps   :   BOOL := TRUE;
bShowMatchValue     :   BOOL := TRUE;
bShowRotationValue  :   BOOL := TRUE;
fMatchThreshold     :   REAL := 0.94;
nProjections        :   UDINT := 15;
nRotations          :   UDINT := 32;
fScaleFactor        :   REAL := 0.225;
eInterpolationType  :   ETcVnInterpolationType := TCVN_IT_BILINEAR;

// Template matching with angle range specific parameters.
nAngles             :   UDINT := 16;
fStartAngle         :   LREAL := -90;
fStopAngle          :   LREAL := 90;

// Template matching results
hrMatch             :   HRESULT;
ipMatches           :   ITcVnContainer;
ipMatchValues       :   ITcVnContainer;

Code

hr := F_VN_SetRoi_TcVnRectangle_UDINT(stMatchingRoi, ipImageIn, hr);

IF bUseRotationSteps THEN
    // Use when object rotation is not specified to cover up to 360° range

    // Valid values for the number of rotations: 1 (0°), 2 (0° and 180°), 4 (0°, 90°, 180°, and 270°), 8 (0°, 45°, ..., 315°), 16 (0°, 22.5°, ..., 337.5°) and 32 (0°, 11.25°, ..., 348.75°)
    // Set value to the next lower valid rotation step
    CASE nRotations OF
        0..1: nRotations := 1;
        2..3: nRotations := 2;
        4..7: nRotations := 4;
        8..15: nRotations := 8;
        16..31: nRotations := 16;
    ELSE
        nRotations := 32;
    END_CASE

    hrMatch := F_VN_FindTemplateWalshExp(
            ipSrcImage          := ipImageIn,
            ipTemplateImage     := ipImageTemplate,
            ipMatches           := ipMatches,
            fMatchThreshold     := fMatchThreshold,
            nProjections        := nProjections,
            eOptions            := TCVN_FTWO_FUSE_MATCHES OR TCVN_FTWO_ROTATED_RECTANGLE,
            nRotations          := nRotations,
            fScaleFactor        := fScaleFactor,
            eInterpolationType  := eInterpolationType,
            ipMatchValues       := ipMatchValues,
            hrPrev              := hr);
ELSE
    // Use when rotation range can be limited (e.g. ±45°)
    hrMatch := F_VN_FindTemplateWalshExp_AngleRange(
            ipSrcImage          := ipImageIn,
            ipTemplateImage     := ipImageTemplate,
            ipMatches           := ipMatches,
            fMatchThreshold     := fMatchThreshold,
            nProjections        := nProjections,
            fStartAngle         := fStartAngle,
            fStopAngle          := fStopAngle,
            nAngles             := nAngles,
            eOptions            := TCVN_FTWO_FUSE_MATCHES OR TCVN_FTWO_ROTATED_RECTANGLE,
            fScaleFactor        := fScaleFactor,
            eInterpolationType  := eInterpolationType,
            ipMatchValues       := ipMatchValues,
            hrPrev              := hr);
END_IF

Auswertung und Zeichnen der Ergebnisse

Zunächst wird überprüft, ob die Funktion erfolgreich ausgeführt wurde und Matches gefunden wurden. Die gefundenen Matches werden iteriert, um die Koordinaten (mit ROI-Offset) und Match-Values auszulesen und in das Ergebnisbild einzuzeichnen. Dafür wird eine Hilfsfunktion F_DrawMatchResult in dem Projekt bereitgestellt, die ein rotiertes Rechteck und einen Pfeil zur Darstellung der Orientierung zeichnet. Zusätzlich werden der Match-Value und der erkannte Rotationswinkel eingeblendet. Über die Variablen bShowMatchValue und bShowRotationValue lassen sich diese Texte ausblenden, falls nur das gedrehte Rechteck visualisiert werden soll.

Variablen

stMatchRectangle    :   TcVnRotatedRectangle;
fMatchValue         :   REAL;
nNumberOfMatches    :   ULINT;
nMatchIdx           :   ULINT;

// Color and Drawing
sStatusText        :   STRING;
aColorGreen        :   TcVnVector4_LREAL := [0, 255, 0];
aColorWhite        :   TcVnVector4_LREAL := [255, 255, 255];
aColorBlack        :   TcVnVector4_LREAL := [0, 0, 0];
aColorOrange       :   TcVnVector4_LREAL := [255, 140, 0];

hr                 :   HRESULT;

Code

IF hrMatch = S_OK THEN
    hr := F_VN_GetNumberOfElements(ipMatches, nNumberOfMatches, hr);

    IF nNumberOfMatches > 0 THEN
        // Write the number of matches into result image
        sStatusText := CONCAT('Matches: ', ULINT_TO_STRING(nNumberOfMatches));

        FOR nMatchIdx := 0 TO nNumberOfMatches - 1 DO
            // Get match region
            hr := F_VN_GetAt_TcVnRotatedRectangle(ipMatches, stMatchRectangle, nMatchIdx, hr);
            // Add ROI offeset
            stMatchRectangle.aCenter[0] := stMatchRectangle.aCenter[0] + UDINT_TO_REAL(stMatchingRoi.nX);
            stMatchRectangle.aCenter[1] := stMatchRectangle.aCenter[1] + UDINT_TO_REAL(stMatchingRoi.nY);

            // Get match value
            hr := F_VN_GetAt_REAL(ipMatchValues, fMatchValue, nMatchIdx, hr);

            // Draw matches on result image
            hr := F_DrawMatchResult(stMatchRectangle, ipImageResult, bShowRotationValue, bShowMatchValue, fMatchValue, 3, 1.2, aColorGreen, aColorOrange, hr);
        END_FOR
    ELSE
        sStatusText := 'No matches';
    END_IF
ELSIF hrMatch = Tc2_System.E_HRESULTAdsErr.NOTFOUND THEN
    sStatusText := 'No matches';
ELSE
    // Last 3 hex digits of HRESULT
    sStatusText := CONCAT('HRESULT: 0x', DWORD_TO_HEXSTR((TO_DWORD(hrMatch) AND 16#FFF), 3, FALSE));
END_IF

// Draw status text
hr := F_VN_PutLabelExp(sStatusText, ipImageResult, 0, 32, 3, 3, TCVN_FT_HERSHEY_PLAIN, aColorBlack, aColorWhite, TCVN_LT_8_CONNECTED, S_OK);

hr := F_VN_ResetRoi(ipImageIn, S_OK);

Ergebnis

Das Ergebnisbild ipImageResultDisp visualisiert die gefundenen Matches, die von den Funktionen über den Container ipMatches ausgegeben wurden. Die verwendete ROI ist in Blau dargestellt.

Mit den im Samplecode hinterlegen Defautwerten wird das "CE"-Zeichen als Template gesucht (sTemplateFileName := 'Template01.png'):

Walsh Template Matching mit Rotation 1:

Das zweite Template (Template02.png) kann mit identischen Parametern im Bild gesucht werden.

Walsh Template Matching mit Rotation 2:

Interessant ist hierbei eine Performance-Analyse durch Auswertung der Ausführungszeit nach dem Funktionsaufruf über die fExecutionTime_ms. Da Template02.png insgesamt fast eine doppelt so große Bildfläche aufweist, wird das Matching in diesem Fall schneller ausgeführt; obwohl die gleichen Parameter verwendet werden.

Grundsätzlich beeinflussen die Wahl der Template-Größe und das -Seitenverhältnis die Rechenzeit:

Als zweiter Anwendungsfall kannTemplate03.png verwendet werden, um z.B. über die Kontakte auf der Klemme zu prüfen, ob das Bauteil vorhanden ist, an welcher Position und in welcher Orientierung es sich befindet. Wenn beispielsweise nur Ausrichtungen von ±60° annehmbar sind, kann die Funktion F_VN_FindTemplateWalshExp_AngleRange verwendet werden, um den Suchbereich gezielt einzuschränken:

bUseRotationSteps   := FALSE; 
fStartAngle         := -60;
fStopAngle          := 60;.

Damit die gesuchten Kontakte bei allen möglichen Rotationen innerhalb der erlaubten ±60° sicher gefunden werden können, muss die ROI entsprechend angepasst und großzügig dimensioniert werden. Gleichzeitig sollte sie so klein wie möglich gehalten werden, um die Ausführungszeit nicht unnötig zu verlängern. Darüber hinaus lässt sich die Anzahl der Prüfwinkel deutlich reduzieren, da die Suche auf einen Bereich von lediglich 120° eingegrenzt wird, was die Rechenzeit minimiert. Für dieses Szenario haben sich folgende Parameter bewährt:

// Adjusted parameters for Template03.png
stMatchingRoi.nX        := 200;
stMatchingRoi.nY        := 10;
stMatchingRoi.nWidth    := 750;
stMatchingRoi.nHeight   := 500;

fMatchThreshold         := 0.94;
nProjections            := 18;
nAngles              := 16;
fScaleFactor            := 0.2;
Walsh Template Matching mit Rotation 3:

Hinweise zur Parametrierung und Optimierung

Um das Sample auf eigene Applikationen, Bilder oder neue Templates zu adaptieren, müssen die Matching-Parameter überprüft und gegebenenfalls angepasst werden. Für die Parametrierung und Fehleranalyse empfehlen sich folgende Schritte: