Bilder

Die Handhabung von Bildern in TwinCAT Vision geschieht durch Bild-Objekte, die verschiedene Interfaces, wie z. B. das ITcVnImage, implementiert haben können. Auf die tatsächlichen Bilddaten des Bild-Objekts erhält man über einen Interface Pointer Zugriff der dann in der SPS zur Verarbeitung verwendet wird. Die Bilder können dabei aus verschiedenen Pixeltypen und Anzahl an Kanälen bestehen, sowie eine nahezu beliebige Größe haben. Die Bilddaten liegen aufgrund der Verarbeitung aus der Echtzeit und den dynamischen Größen im Router-Speicher. Kamerabilder werden nach dem Empfangen direkt dort abgelegt, Bilder von der Festplatte müssen zunächst über einen File Source oder SPS Baustein eingeladen werden und gelangen auf diesem Weg in den Router Speicher. Die Assistenten im Camera Objekt arbeiten nicht mit Bildern aus dem Router Speicher.

Bild Interface von einem Vision-Gerät abfragen

Häufig werden Bilder durch die Aufnahme mit einer Kamera oder durch das Einladen einer Bilddatei erstellt. Dafür stehen folgende Funktionsblöcke zur Verfügung:

Je nach Einstellung des Vision-Geräts kann ein Startup-Vorgang oder ein Bild-Triggern nötig sein. Hier wird nur das Abfragen des Interface Pointers in der SPS dargestellt, über den man Zugriff auf das im Speicher liegende Bild erhält:

VAR
    hr: HRESULT;
    fbVisionDevice: FB_VN_SimpleCameraControl;
END_VAR

hr := fbVisionDevice.GetCurrentImage(ipImageIn);
IF SUCCEEDED(hr) AND ipImageIn <> 0 THEN
    // Process image…
END_IF

Bildformate

Die beiden meist verwendeten Bildformate sind Mono und RGB. In der Kamera wird das übertragene Bildformat unter dem Register PixelFormat eingestellt. Um die anfallende Datenmenge zu reduzieren und so höhere Übertragungsraten realisieren zu können, gibt es auch gepackte oder gefilterte Bildformate, wie z. B. das Bayer Format. Da diese übertragenen Bildformate nicht verwendet werden können bzw. durch den Filter nicht dem Originalbild entsprechen, sollten die Formate direkt nach dem Empfang konvertiert werden.

Dieser Punkt ist besonders zu beachten, wenn Funktionen verwendet werden, die Bildinformationen interpolieren, wie z. B. F_VN_CompensateLensDistortion, F_VN_WarpAffine, F_VN_RotateImageExp oder wie die Funktionen zur Kalibration, die durch das Filterbild gestört werden. Daher wird generell empfohlen, das Bild zuerst in die SPS einzulesen, dort zu konvertieren, um alle nachfolgenden Funktionen auf dem Originalbild anzuwenden.

Bilder erstellen

Es gibt Situationen, in denen ein Bild benötigt wird, welches nicht durch eine Kamera aufgenommen oder vom Dateisystem eingeladen wurde. Zum Beispiel soll ein einfarbiges Bild zur Anzeige von Ergebnissen erzeugt werden. Dafür kann unter Angabe von Bildgröße, Pixeltyp und Anzahl der Kanäle die Funktion F_VN_CreateImage verwendet werden. Der hier gewählte Pixeltyp TCVN_ET_USINT entspricht einem üblichen Bild mit 8-Bit pro Kanal. Es ist zu beachten, dass dabei der für das Bild benötigte Speicher nur allokiert, jedoch nicht initialisiert wird. Daher kann das Bild mit der Funktion F_VN_SetPixels auf eine einheitliche Farbe gesetzt werden.

Bilder 1:

Maximale Bildgröße

Die maximale Bildgröße, die erstellt, empfangen oder verarbeitet werden kann, ist durch die Anzahl von 2^20 Pixel für Zeilen oder Spalten oder durch die Gesamtgröße von 2^30 Pixeln für ein Bild limitiert, je nachdem, welche Grenze zuerst eintritt.

PROGRAM MAIN
VAR
    ipImage     :   ITcVnImage;
    aColorBlack :   TcVnVector4_LREAL := [0, 0, 0];
    hr          :   HRESULT;
END_VAR

hr := F_VN_CreateImage(
    ipImage     :=  ipImage,
    nWidth      :=  640,
    nHeight     :=  480,
    ePixelType  :=  TCVN_ET_USINT,
    nChannelNum :=  3,
    hrPrev      :=  hr
);
hr := F_VN_SetPixels(ipImage, aColorBlack, hr);

Alternativ kann ein Bild mit der Funktion F_VN_CreateImageFromArray auch aus bestehenden Bilddaten erstellt werden.

PROGRAM MAIN
VAR
    aImageData  :   ARRAY [0..479, 0..639] OF USINT;
    ipImage     :   ITcVnImage;
    hr          :   HRESULT;
END_VAR

hr := F_VN_CreateImageFromArray(
    pData       :=  ADR(aImageData),
    ipImage     :=  ipImage,
    nWidth      :=  640,
    nHeight     :=  480,
    ePixelType  :=  TCVN_ET_USINT,
    nChannelNum :=  1,
    hrPrev      :=  hr
);

Zugriff auf Bilddaten

Mit den Funktionen F_VN_GetPixel und F_VN_SetPixel kann auf einzelne Pixel eines Bildes zugegriffen werden. Diese Methode ist einfach anzuwenden. Sie hat allerdings eine relativ hohe Laufzeit, wenn man sie wiederholt auf viele Pixel anwendet. Ich diesem Fall bietet sich vielmehr ein direkter Zugriff auf das Bilddatenarray an. Dazu wird jeweils ein Pointer für eine vollständige Pixelreihe geholt und mit dem Array-Operator auf die einzelnen Pixel zugegriffen:

PROGRAM MAIN
VAR
    ipImage     :   ITcVnImage;
    nHeight     :   UDINT;
    nWidth      :   UDINT;
    y           :   UDINT;
    x           :   UDINT;
    // Pointer type must match pixel type of ipImage!
    pRow        :   POINTER TO USINT;
    nPixelValue :   USINT;
END_VAR

// Create ipImage here or set to an existing image

// Determine width and height of image
hr := F_VN_GetImageWidth(ipImage, nWidth, hr);
hr := F_VN_GetImageHeight(ipImage, nHeight, hr);

// Iterate every pixel of the image
FOR y := 0 TO nHeight - 1 DO
    IF hr = S_OK THEN
        hr := ipImage.GetRowPointer(y, ADR(pRow));
        IF hr = S_OK THEN
            FOR x := 0 TO nWidth - 1 DO
                // Access pixel via pRow[x]
                nPixelValue := pRow[x]; // e.g.
                pRow[x] := nPixelValue; // e.g.
            END_FOR
        END_IF
        // It’s important to release the pointer. Otherwise there will be memory leaks.
        hr := ipImage.ReleaseRowPointer(ADR(pRow));
    END_IF
END_FOR

Anzeigbare Bilder

Um per ADS ein Bild übertragen und anzeigen zu können (z.B. im ADS Image Watch), muss es zunächst in ein anzeigbares Bild des Typs ITcVnDisplayableImage konvertiert werden.

Die strikte Unterscheidung zwischen Anzeige und Verarbeitung von Bildern geschieht aus folgendem Grund: Die Bildübertragung per ADS erfolgt asynchron zum SPS-Zyklus. Folglich könnten zeitgleich zur Bildübertragung Bildverarbeitungsfunktionen ausgeführt werden. Um Speicher-Konflikte während der Bildübertragung zu verhindern, ist es daher notwendig, die Speicherbereiche von anzuzeigenden und zu verarbeitenden Bildern zu trennen.

Die beiden Interfaces ITcVnImage und ITcVnDisplayableImage schließen einen Bildzugriff zum jeweils ungewünschten Zweck (entweder Verarbeiten oder Übertragen) aus. Zur Konvertierung in ein anzeigbares Bild (ITcVnDisplayableImage) stehen folgende Funktionen zur Verfügung:

Die Funktion F_VN_CopyIntoDisplayableImage legt eine tiefe Kopie der Bilddaten an und stellt diese als anzeigbares Bild zur Verfügung. Sie sollte immer dann verwendet werden, wenn das anzuzeigende Bild im folgenden Programmablauf weiterverwendet werden wird:

// Processing, e.g.:
hr := F_VN_Threshold(ipImage, ipImage, 128, 255, TCVN_TT_BINARY, hr);

// Show intermediate processing step
hr := F_VN_CopyIntoDisplayableImage(ipImage, ipImageDisp, hr);

// Further processing, e.g.:
hr := F_VN_FindContours(ipImage, ipContours, hr);

Die Funktion F_VN_TransformIntoDisplayableImage nutzt hingegen die existierenden Bilddaten und gibt den ursprünglichen Interface-Pointer des Bildes frei. Somit können die Bilddaten nicht mehr verarbeitet werden und stehen gänzlich für die Bildübertragung zur Verfügung. Da keine Kopie der Bilddaten erstellt wird, ist die Ausführungszeit dieser Funktion wesentlich kürzer und es wird kein zusätzlicher Speicher verbraucht. Allerdings ist diese Funktion nur möglich, wenn kein weiterer Pointer auf die Bilddaten existiert. Anderenfalls wird der Fehlercode E_INCOMPATIBLE (16#70E) zurückgegeben. Dies kann u.a. dann auftreten, wenn das Ads Communicator Objekt (z.B. zur Stream-Aufnahme) aktiviert ist, da dieses für kurze Zeit das Bild intern weiterhin referenziert. Diese Funktion bietet sich daher hauptsächlich für das Ende einer Verarbeitungskette an, um ein z. B. fertig generiertes Ergebnisbild anzuzeigen.

// Put last results on image, e.g.:
hr := F_VN_DrawContours(ipContours, -1, ipImageRes, aColorRed, 5, hr);

// Finally transform result image into a displayable image
hr := F_VN_TransformIntoDisplayableImage(ipImageRes, ipImageResDisp, hr);

Die Funktion F_VN_TransformIntoDisplayableImageExp bietet darüber hinaus die Möglichkeit, nur bei Bedarf eine tiefe Kopie des Originalbildes zu erzeugen. Somit wird, wenn möglich, die ressourcensparende Transform-Variante genutzt und, wenn nötig, die Copy-Variante. Ein entsprechender Anwendungsfall kann auftreten, wenn das AdsCommunitor-Objekt einer GigE Vision Camera Instanz aktiviert ist, um Bilder als Stream auf das Dateisystem zu speichern. Dabei existiert zusätzlich zum Pointer in der SPS ein weiterer Bilddaten-Pointer im AdsCommunicator-Objekt. Sofern dieser zum Zeitpunkt der Bildverwendung in der SPS noch nicht freigegeben ist, muss das Bild zum Anzeigen kopiert werden.

hr := fbCamera.GetCurrentImage(ipImageIn);
hr := F_VN_CopyImage(ipImageIn, ipImageWork, hr);

// Reliably show original image and try to reuse image data to avoid deep copy
hr := F_VN_TransformIntoDisplayableImageExp(
    ipSrcImage      :=  ipImageIn,
    ipDestImage     :=  ipImageInDisp,
    bAllowDeepCopy  :=  TRUE,
    hrPrev          :=  hr
);

// Manipulate ipImageWork during processing here ...
Bilder 2:

Speicherbegrenzung für anzeigbare Bilder

Bilder die in der Entwicklungsumgebung (ADS Image Watch oder Kamera Assistenten) angezeigt werden, liegen im gleichen Arbeitsspeicherbereich der jeweiligen Visual Studio Instanz. Wenn man nun sehr viele oder große Bilder gleichzeitig anschauen möchte, kann es dabei zu Engpässen durch den maximal verfügbaren Speicher pro Instanz kommen.

Da die Bilder zur Anzeige im ADS Image Watch aus der SPS abgerufen werden (ADS-Kommunikation) und dieser Austausch über den Router Speicher erfolgt, muss an dieser Stelle ebenfalls ausreichend Speicher dafür zur Verfügung stehen.

Meta-Informationen

Mit der Funktion F_VN_GetImageInfo können Meta-Informationen zu einem Bild erlangt werden. Als Ergebnis der Funktion wird eine Struktur vom Typ TcVnImageInfo zurückgegeben. Die darin enthaltenen Informationen können z. B. genutzt werden, um sicherzustellen, dass ein Bild das erwartete Format hat, um konvertiert zu werden:

hr := F_VN_GetImageInfo(ipImageIn, stImageInfo, hr);
IF stImageInfo.stPixelFormat.nChannels = 3 THEN
    hr := F_VN_ConvertColorSpace(ipImageIn, ipImageIn, TCVN_CST_RGB_TO_GRAY, hr);
END_IF