OCR

Dieses Beispiel zeigt, wie Zeichen z.B. von Daten und Chargennummern auf Produktverpackungen mit der OCR-Funktionalität (Optical Character Recognition) erkannt werden können. Der Code veranschaulicht die Anwendung von der Standardfunktion F_VN_OCR als auch der erweiterten F_VN_OCRExp Funktion inklusive der benötigten Vor- und Nachverarbeitung.

Erläuterung

Die Funktionen ermöglichen die Erkennung von alphanumerischen Zeichen in Binärbildern (weiße Zeichen auf schwarzem Hintergrund). Die Klassifizierung basiert auf Machine Learning Modellen mit unterschiedlichem Funktions- und Zeichenumfang.

Die vollständige Beschreibung, die Anforderungen und Einschränkungen sowie weitere Informationen finden Sie im Kapitel OCR und unter den jeweiligen Funktionen.

Modellinitialisierung

Für die OCR-Erkennung muss das zugrundeliegende Zeichenerkennungsmodell vor der ersten Nutzung initialisiert werden. Dies erfolgt über den Funktionsbaustein FB_VN_InitializeFunction, wobei bei eFunction zunächst die zu initialisierende Funktion angegeben werden muss. Mit nOptions werden die Modelle festgelegt, die später beim OCR-Funktionsaufruf verwendet werden sollen. Dabei können mehrere Modelle gleichzeitig mit einem Funktionsaufruf initialisiert werden.

Mit F_VN_CheckFunctionInitialization kann anschließend gecheckt werden, ob das jeweilige Modell initialisiert wurde. Soll das Model zur Laufzeit gewechselt werden oder wird nicht mehr benötigt, kann mit F_VN_DeinitializeFunction das Modell deinitialisiert werden, um so den Speicher wieder freizugeben.

Variablen

bInitialized    : BOOL := FALSE;
fbInit          : FB_VN_InitializeFunction;
nReturnCode     : UDINT;

Code

IF NOT bInitialized THEN
    fbInit(
        eFunction := TCVN_IF_OCR,
        nOptions  := ETcVnOcrModelType.TCVN_OMT_NUMBERS_SC OR ETcVnOcrModelType.TCVN_OMT_UCLETTERS,
        bStart    := TRUE);

    IF NOT fbInit.bBusy THEN
        fbInit(bStart := FALSE);
        IF NOT fbInit.bError THEN
            bInitialized := TRUE;
            nReturnCode := fbInit.nErrorId AND 16#FFF;
        ELSE
            nReturnCode := fbInit.nErrorId AND 16#FFF;
        END_IF
    END_IF
END_IF

Vorverarbeitung

Für die Anwendung der OCR-Funktionen sind einige Vorverarbeitungsschritte notwendig bzw. sinnvoll. Zunächst muss ein ROI definiert werden, die nur die zu erkennenden Zeichen einer Zeile und eine störungsfreie Zone um die Zeichen herum enthält. Des Weiteren muss diese Bildregion in ein 1-kanaliges Binärbild gewandelt werden, dass nur die weißen zu erkennenden Zeichen auf schwarzem Hintergrund enthält. In diesem Beispiel wird dazu die F_VN_Threshold verwendet, die abhängig von dem Hintergrund und der Zeichenfarbe normal oder invertiert angewendet wird.

Da in dem Beispielprojekt Bilder von unterschiedlichen Produkten mit verschiedenen Szenarien verwendet werden, wurden einige Parameter wie z.B. stRoi und fThreshold innerhalb der Funktion F_GetROI für jedes Testbild individuell hinterlegt und anhand des Bildnamens abgerufen. In der Praxis werden die unterschiedlichen Parameterwerte üblicherweise über ein Rezept in der Bedienoberfläche gehändelt. Für die weitere Beschreibung der Beispielanwendung wird das folgende Bild verwendet, auf dem die spezifizierte ROI rot eingezeichnet wurde.

OCR 1:

Zusätzlich zu den notwendigen Vorverarbeitungen können, wie im Beispiel gezeigt, Filteroperationen zur Rauschunterdrückung und morphologische Operationen wie Opening oder Closing angewendet werden, um kleine Störungen zu entfernen und Zeichenformen zu glätten bzw. zu vervollständigen. Häufig werden auch Funktionen zur Kontrastverstärkung verwendet. Die Wahl der geeigneten Filterfunktionen und Parameter hängt stark von den spezifischen Eigenschaften des Bildmaterials ab, daher können noch andere Funktionen z.B. aus ImageColorAndContrastProcessing, ImageFiltering oder ImageSegmentation zur Verbesserung beitragen.

Ein weiterer hilfreicher Schritt kann die Entfernung von Randobjekten mit den Funktionen F_VN_BrightBorderObjects und F_VN_SubtractImages sein. Dadurch werden störende Objekte am Rand der Bildregion eliminiert. Diese entstehen häufig bei ungenauer ROI-Definition, z. B. wenn Teile benachbarter Zeichen oder Zeilen versehentlich im Ausschnitt enthalten sind, insbesondere wenn die Abstände dazwischen recht klein sind.

In dem Beispielprojekt werden folgende Vorverarbeitungsschritte durchgeführt:

OCR 2:

Code

// Set ROI
hr := F_VN_SetRoi_TcVnRectangle_UDINT(stRoi, ipBinaryImage, hr);

// Filter image
hr := F_VN_CreateStructuringElement(ipStructElem, ETcVnStructuringElementShape.TCVN_SES_RECTANGLE, 3,3, hr);
hr := F_VN_MorphologicalOperator(ipBinaryImage, ipBinaryImage, ETcvnMorphologicalOperator.TCVN_MO_OPENING, ipStructElem, hr);

// Binarize image depending on bright or dark text color
IF bInvertImage THEN
    hr := F_VN_Threshold(ipBinaryImage, ipBinaryImage, fThreshold, 255, TCVN_TT_BINARY_INV, hr);
ELSE
    hr := F_VN_Threshold(ipBinaryImage, ipBinaryImage, fThreshold, 255, TCVN_TT_BINARY, hr);
END_IF

// Remove border objects
hr := F_VN_BrightBorderObjects(ipBinaryImage, ipThreshBorder, hr);
hr := F_VN_SubtractImages(ipBinaryImage, ipThreshBorder, ipBinaryImage, hr);

OCR-Anwendung

Nach erfolgreicher Vorverarbeitung kann die OCR-Funktion angewendet werden. Im Beispielprojekt lässt sich über den Parameter bUseExpFunction zwischen der Standard- und der Expert-Version wechseln. Die nachfolgende Beschreibung bezieht sich auf die Anwendung der Expert-Version und die zusätzlichen Möglichkeiten.

Mit dem Parameter sPattern kann das zu erkennende Zeichenformat vorgegeben werden. Diese Möglichkeit ist besonders sinnvoll, wenn die Reihenfolge und der Typ der zu erkennenden Zeichen im Vorfeld bekannt sind. Dann kann für jede Position in der Zeichenkette spezifiziert werden, welches Zeichen erwartet wird. Dadurch hat die Funktion die Information welches Erkennungsmodell pro Zeichen verwendet werden soll, was zu besseren Ergebnissen führen kann. Die spezifischen Modelle enthalten weniger Zeichen, wodurch eine geringer Verwechselungsgefahr entsteht. Weiterhin gibt die Funktion direkt ein S_FALSE zurück, wenn die erkannten Zeichen nicht mit der Vorgabe übereinstimmen.

Für das gezeigte Bild wird das Muster "uu#dddddddd" verwendet, um die Zeichenkette zu erkennen. Dies bedeutet, dass für die ersten zwei Zeichen das Modell TCVN_OMT_UCLETTERS zum Einsatz kommt, um Großbuchstaben zu identifizieren. Anschließend nutzt die Funktion für die folgenden neun Zeichen das Modell TCVN_OMT_NUMBERS_SC, wobei an erster Position ein Sonderzeichen und an den restlichen acht Positionen jeweils eine Ziffer erwartet werden.

Code

hrOCR := F_VN_OCRExp(
            ipSrcImage      := ipBinaryImage,
            eModel          := ETcVnOcrModelType.TCVN_OMT_NUMBERS_SC or ETcVnOcrModelType.TCVN_OMT_UCLETTERS,
            ipCharacters    := ipOCRResult,
            sPattern        := sPattern,
            eOcrOptions     := eOcrOptions,
            ipBoundingBoxes := ipBoundingBoxes,
            ipConfidences   := ipConfidences,
            hrPrev          := hr,
            fMinConfidence  => fMinConfidence);

// Check if characters were found
hr := F_VN_GetNumberOfElements(ipOCRResult, nNumberOfElements, hrOCR);
IF SUCCEEDED(hr) AND nNumberOfElements > 0 THEN
    // Export character to string
    hr := F_VN_ExportSubContainer_String(ipOCRResult, 0, sText, 255, hr);
    // Write text result to image
    hr := F_VN_PutText(sText, ipOriginalImage, stRoi.nX + 5, stRoi.nY + 25, TCVN_FT_HERSHEY_DUPLEX, 1, aGreenColor, hr);
    // Further processing …
END_IF

OCR-Ergebnisauswertung

Nach dem Aufruf der OCR-Funktion sollte zuerst der Rückgabewert HRESULT ausgewertet und geprüft werden, ob oder wie viele Zeichen erkannt wurden. Wenn S_OK zurückgegeben wird, hat die Standardfunktion Zeichen erkannt, bei der Expert Version hängt es davon ab, ob eine sPattern Vorgabe übergeben wurde. Falls es eine Vorgabe gibt, wird nur S_OK zurückgegeben, wenn die erkannten Zeichen damit übereinstimmen. Ansonsten wird S_False zurückgegeben und man kann die erkannten Zeichen selbst weiter untersuchen.

Wenn Zeichen erkannt wurden, können diese in einen String exportiert und z.B. in ein Bild zur visuellen Rückmeldung eingezeichnet werden. Weiterhin kann die Anzahl der Zeichen, der gesamte String oder auch einzelne Zeichen mit hinterlegen Erwartungswerten verglichen werden. Da diese Beurteilungen sehr individuell sind und mit Standard SPS-Funktionen umgesetzt werden können, wird in diesem Beispiel darauf verzichtet.

Bei Verwendung der Expert-Version können zur detaillierteren Analyse noch die einzelnen Klassifizierungskonfidenzen der erkannten Zeichen exportiert, angeschaut und als Akzeptanzkriterien verwendet werden.

Um die Abgrenzungen, Überlappung oder evtl. falsch erkannte Zeichen zuordnen zu können, besteht die Möglichkeit die Bounding Boxen der einzelnen erkannten Zeichen abzurufen, auszuwerten und ein Bild einzuzeichnen. Beim Verrechnen und Einzeichnen muss ggf. die zuvor gesetzte ROI beachtet werden.

Code

// Get number of Confidence elements and export them if array is large enough 
hr := F_VN_GetNumberOfElements(ipConfidences, nNumberOfElements, hr);

// Check if number of elements fits to array size
IF nNumberOfElements > 0 AND nNumberOfElements <= 12 THEN
    hr := F_VN_ExportContainer(ipConfidences, ADR(aConfidences), SIZEOF(aConfidences), hr);
END_IF

// Get bounding box rectangle and draw it to filtered and original image
hr := F_VN_GetNumberOfElements(ipBoundingBoxes, nNumberOfElements, hr);
IF nNumberOfElements > 0 THEN
    FOR nIterator := 0 TO nNumberOfElements -1 DO
        hr := F_VN_GetAt_TcVnRectangle_DINT(ipBoundingBoxes, stRectangle, nIterator, hr);
        hr := F_VN_DrawRectangle_TcVnRectangle_DINT(stRectangle, ipBinaryImage, aWhiteColor, 1, hr);
        // Add ROI Offset
        stRectangle.nX := stRectangle.nX + UDINT_TO_DINT(stRoi.nX);
        stRectangle.nY := stRectangle.nY + UDINT_TO_DINT(stRoi.nY);
        hr := F_VN_DrawRectangle_TcVnRectangle_DINT(stRectangle, ipOriginalImage, aBlueColor, 1, hr);
    END_FOR
END_IF

Ergebnisdarstellung

Das folgende Bild zeigt die Ergebnisdarstellung der Expert-Funktion. In grün werden die erkannten Zeichen eingezeichnet und in blau die optionalen Bounding Boxen.

OCR 3:

Die Konfidenzwerte für jedes erkannte Zeichen stehen zur weiteren Auswertung in dem Array aConfidences zur Verfügung.

OCR 4: