OpenCV 3 mit C++ – Teil 2 – Grundlegende Datentypen

Im letzten Beitrag sind wir das erste Mal der Klasse Mat als begegnet. Sie ist einer der grundlegenden Klassen zur Datenspeicherung in OpenCV. Die Grundlagen derselben wollen wir uns in diesem Artikel anschauen.

Den Code gibt es, wie immer, im Github-Repository zu diesem Projekt.

Grundlagen zur Speicherung von Bildern

Die meisten von euch wissen sicherlich, dass Bilder üblicherweise als Pixelraster gespeichert werden, also als eine Art Tabelle einzelner Bildpunkte. Das gilt zumindest für die sog. Rastergrafiken, der Standard in OpenCV. Vektorgrafiken müssen erst durch externe Bibliotheken konvertiert werden, um anschließend verarbeitet werden zu können.

Vielen bekannt ist sicherlich auch das RGB-Format. Dabei werden für jeden Pixel drei Werte gespeichert: Einer für die Farbe Rot, einer für Blau, einer für Grün. Diese Kanäle werden dann additiv gemischt. Normalerweise hat jeder Kanal eine Größe von einem Byte (8 Bit) und kann somit Werte zwischen 0 (kein Anteil an der Pixelfarbe) und 255 (maximaler Anteil an der Pixelfarbe) annehmen. Häufig kommt auch das RGBA-Format vor, bei dem pro Pixel auch die Information über Alpha, die Deckkraft oder Transparenz gespeichert wird. In OpenCV wird mit dem BGR-Format gearbeitet, hier ist also einfach nur die Reihenfolge der Farbkanäle vertauscht.

HSV-Bilder (für hue, saturationvalue) speichern ebenfalls drei Kanäle: Farbton, Sättigung und Helligkeitswert. Den HSV-Farbraum kann man sich gut vorstellen als Kreiskegel. Auf dessen runder Grundfläche sich ganz außen die voll gesättigten und hellen Farben, je weiter man nach innen wandert, desto geringer ist die Sättigung. In Richtung Scheitel des Kegels nimmt der Helligkeitswert immer weiter ab.

Matrix m(2, 3Der HSV-Farbraum.Hue (H, Farbwert) ist der Winkel, wobei 0° Rot entspricht und die Winkel Richtung Gelb größer werden.

Anders als viele andere Programme und Bibliotheken speichert OpenCV die Werte in anderen Wertebereichen:

  • Hue (H, Farbwert): normalerweise 0 bis 360, in OpenCV 0 bis 180 (ihr verliert also die Hälfte der Farbauflösung)
  • Saturation (S, Sättigung): normalerweise zwischen 0 und 1, in OpenCV 0 bis 255
  • Value (V, Helligkeit): normalerweise zwischen 0 und 1, in OpenCV 0 bis 255

Es ist wichtig, das zu beachten, da die meisten Programme mit diesen Wertebereichen nicht richtig umgehen können bzw. OpenCV nicht mit denen der meiten Programme.

YCbCr-Bilder speichern die Information ebenfalls in drei Kanälen. Im Kanal Y wird die Grundhelligkeit (Luminanz) gespeichert, im Kanal Cb (Blue-Yellow Chrominance) und Cr (Red-Green Chrominance). OpenCV speichert alle drei Kanäle im Wertebereich von 0 bis 255.

YCbCr-CbCr Scaled Y50
Die CbCr-Achse.

Dann gibt noch Graustufenbilder, bei denen keine Farbe gespeichert wird, sondern nur der Graustufenwert. Dafür wird pro Pixel üblicherweise ein Kanal gespeichert, der wieder pro Pixel ein Byte hat, also wieder Werte zwischen 0 und 255 speichern kann.

HDR-Bilder (High Dynamic Range) gewinnen momentan immer weiter an Bedeutung. Sie sind in der Lage, hohe Kontraste (also große Helligkeitsunterschiede) zu speichern und werden immer mehr in der Computergrafik eingesetzt. Die Speicherung ist etwas komplizierter  – üblicherweise werden die Helligkeitswerte logarithmisch gespeichert, um der nichtlinearen Wahrnehmung des menschlichen Auges gerecht zu werden. In OpenCV werden sie z. B. durch das Format OpenEXR, welches im Kompilierprozess eingebunden werden kann, unterstützt. Ich werde sie jedoch vorerst nicht behandeln.

Die grundlegenden Datentypen

Nun schauen wir uns die Datentypen an, die in OpenCV Bilder und mehr repräsentieren. Diese Datentypen kommen alle aus dem Bereich der linearen Algebra. So repräsentiert eine Matrix aus dreidimensionalen Vektoren beispielsweise ein RGB-Bild, eine Matrix aus Skalaren ein Graufstufenbild usw.

1. Matx

Von der Matx-Klasse leiten einige grundlegende Datentypen von OpenCV ab. Zudem ist sie der dynamischen Mat-Klasse sehr ähnlich, es lohnt sich also, sie genauer zu untersuchen.

Matx wird benutzt, wenn Matrizen mit schon zur Kompilationszeit bekannter, also nicht dynamischer Größe benötigt werden – für letzteres wird die sehr ähnliche Klasse Mat verwendet, siehe hierfür weiter unten. Wie die meisten Klassen ist Matx ein Template. Wir müssen dem Template erst den Typ der Elemente übergeben, dann die Anzahl der Zeilen, dann die Anzahl der Spalten.

Damit sieht beispielsweise die Deklaration einer 2×3-Matrix mit float-Einträgen wie folgt aus:

OpenCV unterstützt auch eine Standardausgabe von diesen komplexeren Datentypen mittels cout:

Ausgabe einer 2x3-Matrix.
Ausgabe einer 2×3-Matrix.

Beachtet, dass wir hier die C++-Standardfunktion getchar() verwenden, um das Fenster offenzuhalten. waitkey() gilt nämlich nur für Elemente aus der Highgui-API von OpenCV selbst, nicht also für das Konsolenfenster, und kann somit nicht wie im letzten Beitrag verwendet werden.

Wenn ihr weniger Argumente angebt, als benötigt, um alle Einträge zu füllen, wird der Rest mit Nullen aufgefüllt. Hier ein Beispiel mit Integer-Einträgen:

Ausgabe einer 3x3-Matrix mit aufgefüllten Einträgen
Ausgabe einer 3×3-Matrix mit aufgefüllten Einträgen.

Um die Schreibweise zu verkürzen, gibt es einige typedefs:

Mit

greift man auf das Element in der (i+1)-ten Zeile und der (j+1)-ten Spalte zu (Index nullbasiert), z. B.

Ergebnis: 1.3

Es gibt drei statische Funktionen, um eine Matrix auf einfache Weise zu erstellen: Matx::zeros() erstellt die Nullmatrix, Matx::ones() die Einsmatrix, Matx::eye() die Einheitsmatrix.

Die Einsmatrix, Nullmatrix und Einheitsmatrix.
Die Einsmatrix, Nullmatrix und Einheitsmatrix.

Mathematische Operationen

(Dieser Abschnitt ist teilweise etwas mathelastig. Er kann übersprungen und später bei Bedarf nachgeholt werden.)

Natürlich sind auch mathematische Operationen mit Matrizen sehr wichtig. OpenCV überschreibt einige Operatoren für die intuitive Verwendung der Operationen. So kann eine beliebige Matrix wie folgt mit einem Skalar multipliziert werden:

Multiplikation einer Matrix mit einem Skalar.
Multiplikation einer Matrix mit einem Skalar.

Natürlich lassen sich Matrizen derselben Größe auch addieren.

Addition von zwei Matrizen.
Addition von zwei Matrizen.

An diesem Beispiel sieht man auch, dass sich die Operationen wie bei den primitiven Datentypen verknüpfen lassen.

Natürlich lassen sich Matrizen auch miteinander multiplizieren. Das geht elementweise oder wie die klassische Matrizenmultiplikation. Die elementweise Multiplikation zweier gleich großer Matrizen geschieht mit Hilfe der mul()-Funktion der Klasse Matx:

Elementweise Multiplikation zweier Matrizen.
Elementweise Multiplikation zweier Matrizen.

Die normale Matrizenmultiplikation einer (lxm)– mit einer (mxn)-Matrix geschieht über die Überladung des Multiplikationsoperators:

Matrizenmultiplikation.
Matrizenmultiplikation.

Auch die Vergleichsoperatoren == und != sind definiert. == liefert 1, wenn die Komponenten beider Matrizen vollständig identisch sind, != das invertierte Ergebnis.

Die Determinante einer (quadratischen) Matrix bekommt ihr über die Funktion cv::determinant():

Ergebnis: 4

Die Inverse bildet ihr über die Funktion inv() der Matx-Klasse:

Inverse
Inverse

Ihr könnt ein Argument übergeben, der bestimmt, mit welchem Verfahren die Inverse bestimmt wird. Zur Auswahl stehen DECOMP_SVD, DECOMP_LU, DECOMP_CHOLESKY, die Verfahren werden etwas weiter unten kurz beschrieben.

Lineare Gleichungssysteme lösen:

Natürlich kann man mit OpenCV Gleichungssysteme der Form Ax = b lösen. Dafür erstellen wir eine Matrix A, einen Vektor b und einen leeren Vektor x und lösen das Gleichungssystem mit der solve()-Methode der Klasse Matx.

Lösung des LGS.
Lösung des LGS.

Die Methode solve() bekommt den Vektor b und eine Zahl übergeben, die angibt, mit welchem Algorithmus das Gleichungssystem gelöst werden soll. Welcher Algorithmus benutzt werden kann, hängt davon ab, welche Bedingungen die Matrix A erfüllt. Je mehr man über die Matrix weiß, desto besser, da dann ein umso effizienterer Algorithmus gewählt werden kann:

  • DECOMP_LU: Der Gauß-Algorithmus, realisiert durch LU-Zerlegung (siehe z. B. hier)
  • DECOMP_CHOLESKY: Lösung durch Aufteilung der Matrix in zwei Diagonalmatrizen sowie Vorwärts- und anschließendes Rückwärtseinstzen (siehe hier).
  • DECOMP_QR: Lösung durch die QR-Faktorisierung.
  • DECOMP_EIG: Die Matrix wird mit Hilfe von Eigenwerten diagonalisiert, gelöst, und zurücktransformiert. Dafür muss sie natürlich diagonalisierbar sein.
  • DECOMP_SVD: Lösung mit Hilfe der Singulärwertzerlegung.

Es gibt noch viele weitere nützliche Funktionen zum Diagonalisieren von Matrizen, Berechnen der Nullstellen von Polynomen, etc. Diese können hier und hier näher begutachtet werden.

2. Mat

Die Mat-Klasse ist wohl die wichtigste Klasse in OpenCV, da in einer solchen meist die Bilder gespeichert werden, die untersucht werden sollen. Da wir uns bereits die Matx-Klasse ausführlich angeschaut haben, können wir hier darauf größtenteils verzichten. Der Code der Mat-Klasse kann hier abgerufen werden.

Da jedoch die Größe der Matrix sowie der gespeicherte Typ bei der Mat-Klasse dynamisch sind (also nicht zur Kompilierzeit festgelegt), müssen wir der Matrix bei der Erstellung diese Parameter mitteilen. Einer der zahlreichen Konstruktoren der Klasse Mat sieht dann z. B. so aus (für die anderen Konstruktoren einfach schnell in diese Datei schauen):

Man übergibt also im Konstruktor die Anzahl der Zeilen und Spalten sowie den Typ jeweils als int.  Doch wie sieht der letzt Parameter konkret aus? Um die Zahl zu bestimmen, gibt es das Makro CV_MAKETYPE(depth, cn) in dieser Datei:

Diesem Makro übergibt man die Bittiefe des Datentyps sowie der Anzahl der Kanäle, die pro Matrixeintrag gespeichert werden sollen. Die Bittiefen verschiedener Datentypen sind in derselben Datei definiert:

Natürlich gibt es (in derselben Datei) schon eine Vielzhal an vordefinerten Werten:

So sieht dann die Erstellung einer neuen Mat-Instanz mit 3 Kanälen aus unsigned chars (Bittiefe von 8, perfekt, um ein normales RGB-Bild zu speichern) so aus:

Häufig unterscheiden sich die Methoden der Mat-Klasse von denen der Matx-Klasse nur durch Parameter, deren Notwendigkeit sich durch die mögliche dynamische Änderung der Matrix zur Laufzeit ergibt (wie eben im Konstruktor betrachtet.

Datenstruktur von Mat und Speichermanagement

Wie hoffentlich jeder weiß, muss man beim Speichermanagement in C++ aufpassen, um keine Sicherheitslücken aufzureißen oder memory leaks zu produzieren. Deshalb wollen wir uns kurz die interne Datenstruktur von Mat anschauen.

Mat besteht im Wesentlichen aus zwei Teilen: Einem Header, der Eigenschaften wie die Größe der Matrix, den Datentyp, die Kanalanzahl, etc. angibt, und einem Pointer auf einen Speicherbereich, in dem die eigentlichen Daten liegen. Die Mat-Klasse funktioniert sehr ähnlich wie der neue Shared Pointer in C++ 11 (std::shared_ptr): Die Klasse zählt mit, wie häufig auf diesen Speicherbereich verwiesen wird. Erlischt eine Referenz (z. B. weil eine Funktion verlassen wird), wird der der Referenzzähler heruntergezählt, übergebt ihr einer Funktion ein Mat-Objekt, wird er heraufgezählt. Erreicht der Referenzzähler den Wert 0, wird der Speicherbereich freigegeben. Somit ist also keine manuelle Speicherfreigabe erforderlich. Jedoch gibt es auch die Möglichkeit, eigene Daten an ein Mat-Objekt zu übergeben, welches diese dann nur noch referenziert. In diesem Fall ist man wieder selber verantwortlich für die Freigabe der Daten nach deren Nutzung.

Das ist deshalb sehr sinnvoll, weil man Mat-Objekte sehr häufig an Funktionen übergibt oder auf Teilbereiche (ROIs) verweisen will.

3. Vec

Die Klasse Vec leitet direkt von Matx ab und setzt im Wesentlichen nur die zweite Dimension konstant auf 1:

Auch hier kann also wieder ein Datentyp angegeben werden. Der Code zur Klasse findet sich hier. Auch hier gibt es wieder typedefs, die uns das Leben leichter machen:

Ansonsten gelten die üblichen Rechenregeln:

Die Funktion cv::norm(v) gibt die euklidische Norm des Vektors (Betrag wie aus der Schule) zurück. Auf einzelne Komponenten des Vektors kann mit [] zugegriffen werden.

4. Scalar

Scalar ist eine von der Vec-Klasse abgeleitete Datenstruktur, die im Wesentlichen ein Vektor mit 4 Komponenten darstellt und wie folgt definiert ist:

Auch hier kann also wieder ein Datentyp angegeben werden. Den Code findet ihr hier. Und es gibt wieder ein typedef:

Schreibt man also nur Scalar, so ist ein vierkomponentiger Vector mit double-Einträgen gemeint.

5. Point[3]

Die Point-Klassen leiten nicht von Matx ab. Sie speichern 2 bzw. 3 Koordinaten (x, y, z). Die Klassen können hier gefunden werden. Die Definitionen:

bzw.

Und auch hier gibt es wieder ein paar typedefs:

bzw.

Die Rechenoperationen sind wie gewohnt definiert.

6. Size

Size leitet ebenfalls nicht von Matx ab. Die Klasse speichert einfach die Höhe und die Breite (height und width) und wird in OpenCV sehr häufig verwendet, um die Größe eines Bildes anzugeben. Die Definition (hier zu finden):

Und wieder ein paar typedefs:

Zudem bietet die Klasse ein area()-Funktion, welche die eingeschlossene Fläche zurückgibt.

7. Rect

Rect speichert vier Werte: x, y, width, height und somit alle Informationen, die notwendig sind, um eine Rechteck inklusive Position zu beschreiben. Diese Klasse wird sehr häufig verwendet, um ROIs (Region of Interest) zu beschreiben. Die Definition sieht so aus (hier zu finden):

Wieder einige typedefs:

Die area()-Funktion liefert den Flächeninhalt, size() die Größe vom Typ Size. tl() (top left) und br() (bottom right) geben die obere linke und die untere rechte Ecke des Rechtecks als Points zurück.

8. RotatedRect

Die Klasse RotatedRect repräsentiert ein um einen Mittelpunkt rotiertes Rechteck. Es werden der Mittelpunkt und die Größe als Point2f bzw. Size2f sowie der Rotationswinkel als float gespeichert.

Ihr habt drei Konstruktoren zur Auswahl:

Die ersten beiden sind ohne Erklärung verständlich. Beim dritten werden drei Punkte entweder gegen oder mit dem Uhrzeigersinn angegeben (nicht aber ohne jegliche Reihenfolge), die im Rechteck liegen sollen.

Die Funktionen boundingRect2f() und boundingRect() geben dasjenige nichtrotierte Rechteck (als Rect2i bzw. Rect2f) zurück, welches das rotierte umschließt:

Das Bounding Rect, welches das rotierte Rechteck umschließt.
Das Bounding Rect, welches das rotierte Rechteck umschließt.

9. InputArray und OutputArray

Sehr häufig begegnet man in OpenCV dem Typ InputArray bzw. OutputArray. Diese werden in OpenCV standardmäßig verwendet, wenn ein Bild an eine Funktion übergeben wird. Sie ermöglichen es, dafür nicht nur die Klasse Mat zu verwenden, sondern zum Beispiel auch Matx, einen (Standard-)Vektor von einem (Standard-)Vektor, also vector<vector<int>>, einen OpenGL-Buffer, eine CUDA-GPU-Matrix, etc. Welche Typen unterstütz werden sieht man in der Quelldatei der Mat-Klasse:

Die beiden Klassen vereinheitlichen den Zugriff auf die Daten durch eine Reihe von Methoden, die man ebenfalls in der Datei findet. In der Praxis hat man aber so gut wie nie direkt mit diesen Klassen zu tun, trotzdem ist es wichtig, deren Bedeutung zu kennen. So werden wir beispielsweise später bei den Konturen einen Vektor von Vektoren von Punkten übergeben.

OpenCV 3.x: Windows-Installation von C++ und Python für Visual Studio 2017

Diese Anleitung beschreibt die Installation von OpenCV auf Windows für Visual Studio 2017. Dabei liegt der Fokus auf C/C++, jedoch werden auch die Einstellungen für Python betrachtet.

Diese Anleitung wurde auf Windows 10 Home 10.0.14393 und mit Visual Studio 2017 VisualStudio/15.0.0+26228.12 sowie OpenCV 3.3.0 getestet.

Vorab: Auf OneDrive gibt es eine vorkompilierte Version von mir. Die Unterstützung für verschiedene Plattformen (x64, x86, ARM, jeweils für Windows und Linux) und OpenCV-Versionen kann variieren. Wenn ihr selber für eine neue, noch nicht verfügbare Plattform kompiliert habt, könnt ihr gerne einen Pull-Request schreiben. Zusätzlich gibt es auch vorkompilierte Versionen von OpenCV selbst, diese haben jedoch häufig den Nachteil, dass sie nicht die aktuelle Version von Visual Studio oder eine andere Entwicklungsumgebung unterstützen.

Schritt 1: Quellcode herunterladen

Den aktuellsten Quellcode von OpenCV gibt es auf der Github-Seite der Bibliothek (https://github.com/opencv/opencv). Dieser kann dort entweder als Zip-Datei heruntergeladen werden und anschließend entpackt werden oder mit Git heruntergeladen werden. Die letzte stabile Version kann man sich unter https://github.com/opencv/opencv/releases oder unter http://opencv.org/releases.html als Zip herunterladen (immer den Sourcecode) und entpacken. Ich würde euch empfehlen, den Code in den Ordner C:\OpenCV-[Version] zu speichern.

(Anm.: Ihr habt dort auch die Möglichkeit, eine .exe-Datei herunterzuladen. Diese tut nichts anderes, als den Quellcode in einen angegebenen Ordner zu entpacken. Dabei werden auch vorkompilierte Bibliotheken ausgepackt. Wenn ihr die Bibliothek selber mit zusätzlichen Features kompilieren wollt, könnt ihr löschen. Diese ist zudem meist veraltet und nicht mit der jeweils aktuellsten Version von Visual Studio oder anderen IDEs kompatibel.)

Falls ihr den Code mit Git herunterladen und verwalten wollt, so müsst ihr Git unter https://git-scm.com/downloads herunterladen, anschließend die Git-Bash öffnen. Dort navigiert ihr mit

in den Ordner (in der Git Bash werden die Befehle aus der Bash von Linux verwendet), in dem ihr den Code haben wollt und ladet diesen mit dem Befehl

herunter.

Der Ordner, in dem der Sourcecode liegt, wird ab jetzt „[Euer Source-Ordner]“ genannt.

Schritt 2 (optional): Zusatzbibliotheken

Wie ich bereits im Einstiegstutorial zu OpenCV besprochen hatte, unterstützt OpenCV eine Vielzahl von zusäztlichen Bibliotheken, die entweder zusätzliche/verbesserte Funktionalität oder gesteigerte Effizienz versprechen. Alle folgenden Erweiterungen sind optional! Sie können unabhängig voneinander aktiviert oder deaktiviert werden.

1. Python

Python ist natürlich keine Bibliothek, sondern eine Skriptsprache, mit der man ebenfalls OpenCV programmieren kann. Sowohl Python 2.x als auch 3.x sollten problemlos unterstützt werden. In diesem Tutorial werden wir später bei der Makefile-Konfiguration nur Python 3.x betrachten, die Konfiguration für Python 2.x ist aber analog.

Zunächst einmal muss natürlich Python installiert werden. Den Installer bekommt ihr hier (für Windows, Mac OS X und andere Plattformen). Achtet darauf, wo Python installiert wird, das ist nachher noch wichtig.

Danach muss die Mathematikbibliothek numpy installiert. Gebt dafür in der Kommandozeile

ein. Alternativ könnt ihr sie hier herunterladen.

2. Intel Threading Building Blocks (TBB)

Die Intel Threading Building Blocks ist eine von Intel entwickelte C++-Bibliothek zur effizienten Programmierung von Threads zur Nutzung von Mehrkernprozessoren. Herunterladen könnt ihr euch die Bibliothek hier. Dort ladet ihr euch die Datei „[Kryptische Zahlenkombination]oss_win.zip“ herunter. Erstellt dann den Ordner „dep“ (für dependencies, natürlich ist auch ein anderer Name möglich) im OpenCV-Verzeichnis und entpackt die Zip-Datei dorthinein (bei mir ist der Ordner z. B. „C:\OpenCV-3.3.0\sources\dep\tbb2017_20170226oss“).

3. Intel Integrated Performance Primitives (IPP)

Achtung: Diese Bibliothek kostet Geld, und das nicht zu knapp (etwa 200€)!

Auch die IPP ist eine Bibliothek von Intel, die einige Besonderheiten der Intel-Prozessoren für bessere Performance und besseres Multithreading nutzt.

Die Bibliothek ladet ihr hier herunter und entpackt sie in den „dep“-Ordner von gerade eben.

4. Eigen

Eigen ist eine C++-Bibliothek für Lineare Algebra (der Name kommt von den sog. Eigenwerten). OpenCV kann diese Bibliothek teilweise für Optimierungen nutzen. Auch sie ladet ihr einfach hier herunter und entpackt sie dann in den „dep“-Ordner.

5. CUDA

Achtung: CUDA läuft nur auf (bestimmten) GPUs von Nvidia! Dementsprechend lässt sich diese Erweiterung nur auf Computern mit einer unterstützen Grafikkarte von Nvidia installieren und nutzen. Da ich keine solche besitze, kann ich diesen Teil leider nicht testen. Sollte jemand von euch dazu in der Lage sein und Fehler in der Anleitung feststellen, so möge er/sie mir seine/ihre Ergebnisse bitte mitteilen.

Durch CUDA können rechenaufwendige Programmteile in der GPU (Grafikkarte) berechnet werden, die für einige Aufgaben viel besser optimiert ist als eine General Purpose CPU. Durch den Einsatz von CUDA kann sich die Performance locker um einen Faktor 20 oder höher steigern lassen. Der Nachteil ist, dass CUDA – anders als OpenCL – nur auf unterstützten Nvidia-GPUs läuft. Außerdem sieht ein OpenCV-Programm, welches die CUDA-Funktionalität nutzt, ein wenig anders aus, d. h., es ist ein Codeanpassung notwendig, um diesen Performanceboost zu bekommen. Die Mühe lohnt sich aber wirklich. Grund für die Geschwindigkeit ist, dass bei Bilderkennung sehr viele einfache, meist voneinander unabhängige Rechenoperationen ausgeführt werden. Genau dafür sind Grafikkarten, die zwar sehr viele, dafür aber weniger leistungsstarke Prozessorkerne haben, gemacht, da für Computerspiele genau die gleichen Anforderungen bestehen.

Um CUDA zu installieren, wählt ihr hier euren Installer aus. Wichtig ist, dass ihr bei der Installation die volle Installation wählt (dafür „Benutzerdefiniert“ wählen und dann alle Pakete markieren)!

Beim CUDA-Installer "Benutzerdefiniert" auswählen.
Beim CUDA-Installer „Benutzerdefiniert“ auswählen.
Alle Pakete auswählen.
Alle Pakete auswählen.

Schritt 3: Makefile konfigurieren

Nun müssen aus dem Code Bibliotheken erzeugt werden, welche später in Visual Studio eingebunden werden können. Dafür benötigen wir einen C++-Compiler. Visual Studio bringt einen solchen von Haus aus mit.

Wie genau der Sourcecode kompiliert wird, das regelt ein sogenannte Makefile, welche von einem Make-Programm eingelesen wird, welches dann die Kompilation automatisiert. Bei OpenCV wird hierfür CMake (für cross-platform make) eingesetzt. Dieses müsst ihr euch erst installieren. Dafür könnt ihr euch hier den Installer für Windows herunterladen. Ihr könnt die PATH-Variable zwar setzen lassen (das ermöglicht später einen bequemen Zugriff in der Command Line), müsst es aber nicht.

Die Aktivierung von "Add CMake to the system PATH for all/the current users" ist optional.
Die Aktivierung von „Add CMake to the system PATH for all/the current users“ ist optional.

Startet nun die CMake-GUI als Administrator. Ihr werdet durch einen Bildschirm begrüßt, der in etwa so aussieht:

CMake Begrüßungsfenster
Das CMake Begrüßungsfenster

Klickt nun auf „Browse Source…“ und wählt den Ordner mit dem Sourcecode von OpenCV aus (bei mir „C:\OpenCV-3.3.0\sources“). Klickt dann auf „Browse Build…“ und wählt den Ordner aus, in dem ihr die von CMake erzeugten Projektdateien haben wollt (in meinem Fall „C:\OpenCV-3.3.0\build“).

Wählt die entsprechenden Ordner aus.

Klickt anschließend auf „Configure“. Ihr werdet daraufhin gefragt, welchen Generator ihr für das Projekt haben wollt. Da wir das Projekt anschließend mit Visual Studio 2017 kompilieren wollen, wählen wir hier „Visual Studio 15 2017 [Plattform]“ aus (das 15 ist die interne Version von Visual Studio, hier wird wirklich Visual Studio 2017 ausgewählt). [Plattform] gibt an, für welchen Prozessortypen ihr das Projekt erstellen wollt. „Win64“ ist 64-Bit Windows, „ARM“ ist eben ARM (für Tablets oder Smartphones und andere ARM-Geräte), für 32-Bit Windows steht nichts dahinter. Das Konfigurieren dauert eine Weile, weil ggf. fehlende Bibliotheken heruntergeladen werden. Danach sollte es in etwa so aussehen:

Wählt den CMake Generator aus.
Wählt den CMake Generator (hier für 64-Bit Windows) aus.

Sobald ihr auf „Finish“ klickt, liest CMake die Makefiles, die mit OpenCV mitgeliefert werden, ein und erstellt daraus eine GUI zum Konfigurieren der Kompilation. Das kann eine kleine Weile dauern, danach sollte es etwa so aussehen:

CMake hat die Konfiguration geladen.
CMake hat die Konfiguration geladen.

Durch die Aktivierung des Häkchens „Grouped“ bekommt ihr eine bessere Übersicht, in der die Variablen des Makefiles gruppiert sind. Hier habt hier nun die Möglichkeit, die Variablen der Makefile zu verändern. Das sind teilweise einfache booleans, teilweise auch Pfäde zu den Bibliotheken, die ihr einbinden wollt. Sucht nun nach dem Wert „BUILD_opencv_world“ und setzt ihn auf true (Häkchen aktivieren). Dieser Schritt bewirkt, dass alle Teilbibliotheken von OpenCV – das sind ziemlich viele – in einer einzigen vereint werden, der „opencv_world“. Dieser Schritt ist optional, ich würde ihn aber dringend empfehlen, da euch das später viel Tipparbeit erspart. Erst, wenn es an die Auslieferung eures Programms gehen sollte und ihr genau wisst, welche Bibliotheken ihr braucht, ist es sinnvoll, nur diejenigen einzubinden, welche notwendig sind. Dieses Tutorial wird davon ausgehen, dass ihr „BUILD_opencv_world“ aktiviert habt.

Grundsätzlich seid ihr jetzt schon fertig mit der Konfiguration des Projektes. Für diejenigen unter euch, die noch weitere Bibliotheken einbinden wollen, gibt es jetzt wieder einen Abschnitt, den alle anderen überspringen können.

Konfiguration für die Einbindung von weiteren Bibliotheken

Die Makefile ist in der Lage, die von OpenCV unterstützten Bibliotheken größtenteils automatisch zu detektieren und die Variablen entsprechend zu setzen. Misslingt das, wird eine NOT_FOUND-Konstante gesetzt, die wir ersetzen müssen. Wir werden nun durch die wichtigsten Optionen gehen und das Makefile entsprechend konfigurieren.

1. Python

Wollt ihr die Python-Unterstützung kompilieren, dann müsst ihr in der Makefile einige Pfade setzen. Gebt zum Filtern der Variablen in der CMake-Suchleiste „python“ ein.

Konfiguration von Python in CMake.

Alle Pfade und Dateien findet ihr im Installationspfad von Python. Wenn ihr den nicht mehr wisst, öffnet die Python-Konsole und gebt folgendes ein:

Python: Gebe Installationsverzeichnis aus
Python: Gebe Installationsverzeichnis aus

Habt ihr den Hauptfad einmal bestimmt, sind die weiteren Pfade relativ klar. Die packages befinden sich in „Lib\site-packages“, in einem entsprechenden Unterverzeichnis davon befindet sich normalerweise auch numpy. Falls nicht, könnt ihr ihn mit der Eingabe von

in der Python-Konsole herausfinden.

2. Intel Threading Building Blocks (TBB)

Sucht nach der Variablen „WITH_TBB“ und aktiviert sie. Passt auf, dass ihr nicht „BUILD_TBB“ auswählt (wird unter Windows in der x64- und x86-Variante nicht unterstützt, ARM sollte allerdings funktionieren)! Wählt dann wieder „Configure“. Danach sollten die Variablen „TTB_ENV_INCLUDE“, „TBB_ENV_LIB“, „TBB_ENV_LIB_DEBUG“ und „TBB_VER_FILE“ auftauchen (oder ähnlich, das ändert sich ab und zu). Bei jeder Variable könnt ihr einen Pfad bzw. eine Datei auswählen. Klickt dazu auf die drei Punkte und navigiert zu den entsprechenden Verzeichnissen oder Dateien:

  • TBB_ENV_INCLUDE: „[Euer dep-Verzeichnis]/dep/tbbxxxx_xxxxxxxoss/include“
  • TBB_ENV_LIB: „[Euer dep-Verzeichnis]/dep/tbbxxxx_xxxxxxxoss/lib/[arch]/vcxx/tbb.lib“
  • TBB_ENV_LIB_DEBUG: „[Euer dep-Verzeichnis]/dep/tbbxxxx_xxxxxxxoss/lib/[arch]/vcxx/tbb_debug.lib“
  • TBB_VER_FILE: „[Euer dep-Verzeichnis]/dep/tbbxxxx_xxxxxxxoss/include/tbb/tbb_stddef.h“

Wichtig: Wählt nicht den „vcxx_ui“-Ordner aus, darin befinden sich Bibliotheken für Windows Store Apps (für nähere Informationen siehe hier).

„vcxx“ ist im Falle von Visual Studio 2017 „vc14“ (siehe hier für aktuellste Informationen). Für [arch] könnt ihr entweder „ia32“ für die IA-32-Architektur oder „intel64“ für die Intel-64-Architektur auswählen. Bei mir sieht die Konfiguration so aus:

TBB CMake Konfiguration
TBB CMake Konfiguration

3. Intel Integrated Performance Primitives (IPP)

Wie schon gesagt, ist IPP kostenpflichtig. Deshalb habe ich diesen Teil nicht selber getestet und verlasse mich auf die Beschreibungen anderer. Solltet ihr IPP gekauft haben und Fehler in der Beschreibung finden, so bitte ich euch, diese in den Kommentaren mitzuteilen.

Nach der Installation von IPP muss eine Umgebungsvariable namens IPP_ROOT gesetzt werden (evtl. existiert im Installer eine solche Funktion). Danach muss in CMake einfach nur die Option WITH_IPP aktiviert werden.

4. Eigen

WITH_EIGEN muss aktiviert werden, EIGEN_INCLUDE_PATH muss auf den Pfad zur Eigen-Bibliothek gesetzt werden. Dabei ist es wichtig, den Ordner „eigen-eigen-<kryptischer Hash>“ und nicht den darunter liegenden Ordner „Eigen“ auszuwählen.

CMake-Konfiguration für Eigen

5. CUDA

Da ich keine Grafikkarte von Nvidia besitze, mit der ich CUDA testen könnte, kann ich hier leider nur auf die Berichte anderer setzen. Findet ihr Fehler oder besitzt eine Nvidia Grafikkarte, so meldet euch bitte einfach in den Kommentaren.

Auch hier haben alle für CUDA relevanten Einstellungen „CUDA_“ als Präfix. Setzt die entsprechenden Pfade und aktiviert WITH_CUDA.

Normalerweise hat CUDA_SDK_ROOT_DIR den Wert „C:/Program Files/NVIDIA GPU Computing Toolkit/CUDA Tools SDK/vx.x“, CUDA_TOOLKIT_ROOT_DIR den Wert „C:/Program Files/NVDIDA GPU Computing Toolkit/CUDA/vx.x“, wobei „vx.x“ die aktuelle Version ist.

CUDA CMake Konfiguration
CUDA CMake Konfiguration

Schritt 4: Kompilation

Was jetzt noch zu tun bleibt, ist auf den Button „Generate“ zu klicken. Daraufhin werden die erforderlichen Projektmappen für Visual Studio im angegebenen Build-Ordner erstellt. Entweder ihr wechselt im Windows Explorer in das entsprechende Verzeichnis und öffnet die Visual Studio Solution „OpenCV.sln“ mit Visual Studio 2017, oder ihr klickt auf „Open“ in CMake.

Der Build-Ordner, in dem das Ergebnis von CMake liegt.
Wählt die Datei „OpenCV.sln“ aus und öffnet sie in Visual Studio 2017.

Wie ihr seht, besteht diese Solution aus vielen Unterprojekten in verschiedenen Unterordnern. Der einzig wichtige ist der Ordner „CMakeTargets“. Wer sich ein bisschen mit Makefiles auskennt, wird erkennen, dass hier die Targets der Makefile als Projekte auftreten. Standardmäßig ausgewählt ist das Target/Projekt „ALL_BUILD“. Wir wählen „Debug“ aus, klicken rechts auf das Projekt und klicken dann auf „Erstellen“ (bzw. „Build“ in der englischen Version“).

Wählt "Debug" aus, klickt rechts auf das Projekt und klickt dann "Erstellen".
Wählt „Debug“ aus, klickt rechts auf das Projekt und klickt dann „Erstellen“.

Daraufhin werden die Bibliotheken für das Debugging kompiliert. Das kann einige Minuten dauern. Ist der Prozess fertig, findet ihr im Ordner „[Euer Build-Ordner]\lib\Debug“ einige .lib-, .pdb- und .exp-Dateien. Nun stellen wir auf „Release“ um und klicken wieder „Erstellen“, um die Bibliotheken für die Release-Variante zu erstellen. Auch das dauert wieder einige Minuten. Danach ist ein Ordner „[Euer Build-Ordner]\lib\Release“ hinzugekommen, in dem sich ebenfalls Dateien befinden.

Nun kompiliert das Projekt „INSTALL“ ebenfalls sowohl im Debug- als auch im Release-Modus auf die gleiche Weise (das sollte jetzt deutlich schneller gehen). Dadurch wird ein weiterer Ordner „[Euer Build-Ordner]\install“ erzeugt. Je nach ausgewählter Plattform entsteht ein Ordner „x64“, „x86“ oder „ARM“, in dem wiederum ein Ordner „vcxx“ steht (aktuell „vc15“ für Visual Studio 2017). Darin sollte ein „bin“- und ein „lib“-Ordner existieren, die .dll- bzw. .lib-Dateien enthalten (wenn ihr „BUILD_opencv_world“ aktiviert habt, befindet sich hier auch die Datei „opencv_worldxxx[d].dll“ bzw. „opencv_worldxxx[d].lib“), jeweils einmal mit und einmal ohne das Postfix „d“ (für „Debug“). Habt ihr die Python-Option aktiviert, sollte sich im „bin“-Ordner auch ein „python“-Ordner mit einer Datei, die ihr in das Bibliotheksverzeichnis eurer Python-Installation kopieren müsst.

Schritt 5: Visual Studio einrichten

Nun müssen wir Visual Studio einrichten.

Für den folgenden Schritt braucht ihr Administratorrechte. Habt ihr die nicht, schaut etwas weiter unten. Falls ihr eure Anwendung weitergeben wollt, ist der zweite Weg wahrscheinlich ebenfalls der bessere.

Mit Administratorrechten

Zuerst müssen wir das Verzeichnis mit den .dll-Dateien in die PATH-Variable von Windows einbinden. Dafür geht ihr in der (klassischen) Systemsteuerung auf System und Sicherheit -> System -> Erweiterte Systemeinstellungen.

Systemeigenschaften
Systemeigenschaften

Dann klickt ihr auf Umgebungsvariablen. Dort wählt ihr dann Path.

Systemvariablen
Systemvariablen

Dann klickt ihr auf Bearbeiten.

Umgebungsvariable bearbeiten
Umgebungsvariable bearbeiten

Jetzt klickt ihr auf Neu und fügt den Pfad zum bin-Ordner von OpenCV ein, wo sich die .dlls befinden. OK beendet die Sache.

Wenn ihr mit TBB kompiliert habt, müsst ihr außerdem den Pfad zu den .dlls von TBB angeben. Diesen findet ihr in eurem TBB-Ordner (den ihr im dep-Verzeichnis für die Kompilation angelegt habt) und dann im Unterordner „bin\<Prozessorplattform>\<Visual Studio Compiler>“.

Ohne Administratorrechte

Solltet ihr keine Administratorrechte haben, so könnt ihr die .dll-Dateien ins Ausgabeverzeichnis (das Verzeichnis, in dem die .exe-Dateien landen) eures Projekts kopieren. Den Dateipfad findet ihr in der Titelleiste der Konsole.

Wenn ihr mit TBB kompiliert habt, müsst ihr außerdem die passenden TBB-dlls in den Ausgabeordner kopieren. Diese findet ihr normalerweise in eurem TBB-Ordner (den ihr im dep-Verzeichnis für die Kompilation angelegt habt) und dann im Unterordner „bin\<Prozessorplattform>\<Visual Studio Compiler>“. Je nach Konfiguration eures Projekts müsst ihr natürlich die Debug-Version oder die Release-Version kopieren.

Weiter geht es:

Sobald ihr ein Konsolen-Projekt erstellt habt (siehe dazu dieses Tutorial), müsst ihr in die Projekteigenschaften gehen.

Projekteigenschaften aufrufen
Projekteigenschaften aufrufen

Achtet darauf, dass verschiedene Konfigurationen bereitstehen, also die möglichen Kombinationen aus Debug/Release und den verschiedenen Plattformen. Ihr müsst die folgenden Einstellungen für alle möglichen Konfigurationen, die ihr später benutzen wollt, wiederholt treffen.

Ihr klickt nun auf C/C++ -> Allgemein. Bei Zusätzliche Includeverzeichnisse klickt ihr auf Bearbeiten.

Projekteigenschaften, allgemeine C++-Eigenschaften.
Projekteigenschaften, allgemeine C++-Eigenschaften

Dann klickt ihr auf das Ordnersymbol mit dem Stern. Mit dem „…“-Button wählt ihr dann im Dialog den include-Pfad aus, dann auf OK.

Include-Pfad setzen
Include-Pfad setzen

Anschließend müssen wir die allgemeinen Linker-Einstellungen ändern. Dort müsst ihr den Pfad für Zusätzliche Bibliotheksverzeichnisse aufden lib-Ordner von OpenCV stellen.

Allgemeine Linkereinstellungen
Allgemeine Linkereinstellungen

Als letztes muss die Linker-Einstellung Eingabe geändert werden, und zwar die Einstellung Zusätzliche Abhängigkeiten. Abhängig von der Konfiguration zu Debug/Release müsst ihr dort nun „opencv_worldxxx.lib“ oder „opencv_worldxxxd.lib“ eintragen, wobei xxx wieder für die Version steht, im Falle von OpenCV 3.3 steht dort also 330.

Habt ihr mit Eigen kompiliert, und wollt die Eigen-Bibliothek nutzen, dann müsst ihr als zusätzlichen Include-Pfad den Pfad zu den Quelldateien von Eigen angeben. Dies ist normalerweise genau derselbe Pfad, den ihr auch in CMake angegeben habt. Da Eigen nur aus Headern besteht, gibt es keine Bibliotheken u. a. m. zu verlinken.

7. Ende

So, das war es jetzt. Wir sind fertig mit der Einrichtung. Wie es weitergeht, seht ihr in diesem Beitrag, dem ersten Beitrag einer Serie, in der es um OpenCV geht.

OpenCV 3 mit C++ – Teil 1 – Einführung

In dieser neuen Serie wollen wir uns einem spannenden, zukunftsträchtigen, aber auch sehr anspruchsvollen Thema widmen: Dem maschinellen Sehen oder auf englisch: Computer Vision.

Einleitung

Dabei geht es darum, dem Computer so zu programmieren, dass er Bilder (in den meisten Fällen von einer Kamera) auf bestimmte Eigenschaften hin untersuchen kann und abhängig von den Ergebnissen agieren kann. Das momentan sicherlich berühmteste Beispiel ist das autonome Fahren von Automobilen, aber auch Roboter und viele Systeme in der Industrie setzen auf das maschinelle Sehen auf. So soll das Programm beispielsweise Gesichter und Straßenschilder erkennen, fehlerhafte Produkte aussortieren oder das Glasdach eines Autos passend aufkleben.

Schon seit langer Zeit ist dabei eine Programmbibliothek sehr populär und bewährt: OpenCV. Das ursprünglich von Intel initiierte Projekt enthält mittlerweile Beiträge vieler großer Firmen und Universiäten sowie Privatpersonen und wird mittlerweile von der WillowGarage verwaltet, die unter anderem auch das populäre Robot Operating System (ROS) entwickelt. Der Source Code von OpenCV steht unter der BSD License und ist auf GitHub frei einsehbar. Es darf somit auch für kommerzielle Projekte verwendet werden (keine Garantie für diese Angaben, bitte diese Punkte vor Programmveröffentlichung nochmals selber prüfen). Einen populären Auftritt hatte OpenCV bei der DARPA-Challenge im Jahre 2005. Auch Tesla verwendet den Stellenausschreibungen nach zu urteilen OpenCV für den „Autopilot“.

OpenCV ist in C/C++ geschrieben (früher hauptsächlich C, in den letzten Jahren ging es immer mehr in Richtung C++ und Objektorientierung), es existieren aber auch zahlreiche Wrapper für andere Sprachen. Der populärste ist sicherlich der für Python. Der Code ist im höchsten Maße auf Effizienz getrimmt, da diese bei so vielen und aufwendigen Rechnungen auch bei der heutigen Leistungsfähigkeit von Computer sehr wichtig ist. Zudem gibt es die Möglichkeit, beim Kompilieren Bibliotheken wie CUDA (Nvidia), Integrated Performance Primitives (IPP, Intel) oder OpenCL einzubinden, um das letzte bisschen Performance aus dem Computer zu kitzeln. Um diese Bibliotheken zu benutzen, ist allerdings teilweise zusätzliche Arbeit bzw. veränderter Code notwenidg. Außerdem ist OpenCV auf allen wichtigen Plattformen (Windows, Linux, Unix, Mac OS, Android, iOS, Windows Store) lauffähig.

Installation

Da die Installation bei OpenCV wahrscheinlich das Schwierigste ist (zumindest für Einsteiger, die keine Ahnung von cmake o. Ä. haben), findet ihr auf der Seite für OpenCV eine Übersicht über Blogeinträge, welche die Installation mittels verschiedener Methoden und auf verschiedenen Plattformen behandeln (die Anleitungen kommen nach und nach dazu). Sollte eure noch fehlen, sagt mir Bescheid.

Die ersten beiden Testprogramme

Nachdem die Installation nun (hoffentlich) erfolgreich vonstatten gegangen ist, möchten wir natürlich ein erstes kleines Testprogramm schreiben. Dieses soll nichts anderes machen, als ein Bild aus einer Datei zu laden und dieses so lange anzuzeigen, bis der Benutzer eine Taste drückt.

Ich lade das Projekt auf Github hoch, damit ihr Zugriff auf den gesamten Code habt.

Erstes Programm

Zunächst erstellen wir ein Projekt in Visual Studio. Wählt unter „Visual C++“ „Win32-Konsolenanwendung“ aus und gebt einen Namen für die Projektmappe ein.

Neues Projekt erstellen
Neues Projekt erstellen

Danach taucht ein Dialog zum Erstellen des Projekts auf. Klickt zunächst auf „Weiter“.

Konsolenapplikation Schritt 1
Konsolenapplikation Schritt 1

Im nächsten Fenster klickt ihr auf „Leeres Projekt“ und dann auf „Fertig stellen“.

Konsolenapplikation Teil 2
Konsolenapplikation Teil 2

Im nächsten Schritt fügen Sie unter dem Ordner „Quelldateien“ eine Datei „main.cpp“ hinzu.

Datei hinzufügen Teil 1
Datei hinzufügen Teil 1
Datei hinzufügen Teil 2
Datei hinzufügen Teil 2

Die weitere Einrichtung von Visual Studio findet ihr hier im Abschnitt 5.

Zunächst schreiben wir dafür das Standard-Codegerüst für ein C++-Programm.

Danach müssen wir die include-Direktiven für OpenCV einfügen, um die notwendigen Funktionen zu laden. Wir binden zwei .hpp-Dateien ein:

Um auf die Klassen von OpenCV nicht immer mit cv::Klasse zugreifen zu müssen, binden wir den Namespace cv ein.

Bilder werden in der grundlegenden Datenstruktur Mat (für Matrix) gespeichert – dazu im nächsten Teil mehr. Die Funktion imload() lädt ein Bild aus einer Datei und gibt es als Mat zurück. Der Funktion imload() übergibt man den relativen Dateipfad zum Bild. Im Ordner der Projektmappe habe ich dazu einen Ordner „Images“ erstellt und dort ein Bild vom Hubble-Teleskop gespeichert. Somit sieht der Code wie folgt aus:

Bevor wir weitermachen, müssen wir sicherstellen, dass die Matrix nicht leer ist. Wir verlassen die Funktion mit einem Fehlercode, falls die Matrix leer ist.

Wir gehen also aus dem Projektordner in den Projektmappenexplorer, dann ins Verzeichnis „Hubble“ und laden dann die Datei „Hubble.jpg“. Danach erstellen wir ein beschriftetes Fenster mit der Funktion namedWindow(). Ihr übergibt man eine Zeichenkette, die gleichzeitig der Beschriftung wie auch der Identifikation des Fensters dient. Da wir die Zeichenkette also häufiger brauchen und Schreibfehler nicht so toll sind, erstellen wir eine Konstante, die wir dann übergeben.

Nun wollen wir das Bild im Fenster anzeigen lassen. Das geht mit der Funktion imshow(), der der Fenstername sowie das Bild übergeben wird.

Nun müssen wir nur noch verhindern, dass sich das Fenster sofort wieder schließt. Dafür gibt es die Methode waitKey(), die, wenn wir sie ohne Argumente oder mit dem Argument 0 aufrufen, auf das Drücken einer beliebigen Taste wartet und die Taste als char zurückgibt, die wir aber nicht brauchen.

Und hier nochmal der gesamt Programmcode:

Bevor das Programm nun gestartet wird, müssen wir die Entwicklungsumgebung einrichten. Wie das in Visual Studio (2017) gemacht wird, das zeige ich hier in Schritt 5. Danach könnt ihr das Programm starten.

Zweites Programm

Das zweite Programm soll die Kamera, die heutzutage ja in den meisten Laptops verbaut ist, auslesen, uns also wie eine Kamera-App den Videostream zeigen. Dafür essentiell ist die Klasse VideoCapture. Mit

wird auf die erste Kamera zugegriffen. Mit 1, 2, … greift ihr auf die zweite, dritte oder x-te Kamera zu. Wir müssen wieder prüfen, ob der Stream geöffnet ist, ansonsten verlassen wir die Funktion wieder mit einem Fehlercode:

Mit

wird der aktuelle Frame des Streams in eine Matrix geschrieben. Diese Zeile rufen wir in einer while-Schleife auf.

Dieses Programm kann nur mit der Taste ‚q‘ beendet werden. Durch waitKey()-Methode mit dem Parameter 33 wartet die Methode 33 Millisekunden, falls während dieser Zeit ‚q‘ gedrückt, gibt die Methode eben dieses Zeichen zurück. Falls das passiert, springen wir aus der Schleife und beenden somit das Programm. Das Ausführen sollte jetzt ein Fenster mit einem Livestream der Webcam auftauchen.

Ende

Im nächsten Artikel werden wir die grundlegenden Datentypen und Datenspeicherung von OpenCV betrachten.