diff --git a/chap/ausblick.tex b/chap/ausblick.tex index bc8451bb4fa56d00f8a4ca90a176e1143907e939..ba3eca63f4626eea8c22750547d26a2c381f598f 100644 --- a/chap/ausblick.tex +++ b/chap/ausblick.tex @@ -17,7 +17,7 @@ Dies ist möglicherweise bereits Teileweise durch den vorherigen Punkt mit abgedeckt, bedarf aber trotzdem einer weiteren Erklärung. Derzeit werden vor allem bei weiter von der Kamera entfernten Linien, also vor allem den Spurmarkierungen benachbarter Spuren, viele - Fehlklassifizierungen durchgeführt. Wie in \autoref{fig: schlechte kante} gezeigt wird eine Kante durch fehlklassifizierte Pixel unterbrochen, + Fehlklassifizierungen durchgeführt. Wie in \autoref{fig: schlechte kante} gezeigt wird eine Kante durch fehlklassifizierte \gls{Pixel} unterbrochen, sodass eine durchgängige Linienverfolgung nicht möglich ist. Hier könnten zuerst einmal Ansätze zum Verbinden von sehr eng zusammen liegenden Linien Abhilfe schaffen. Aber eine Verbesserung der diff --git a/chap/implementation.tex b/chap/implementation.tex index ae9fcd49224467f5a2cedb1ec11b45fd5da3d49f..9d5e313a99c576c4ed6f395ba86c41b4ff767b5b 100644 --- a/chap/implementation.tex +++ b/chap/implementation.tex @@ -51,9 +51,9 @@ Kegelgröße und der Wert für $\sigma$ übergeben wird. Die eigentliche Kantenerkennung wird mittels eines \glspl{canny} durchgeführt. Dabei handelt es sich um einen von John Canny 19983 - entwickelten und in \cite{Canny:computationAlapproachEdgeDetection} veröffentlichten Algorithmus. Dieser bestimmt für jeden Pixel den - Gradientenbetrag der Gradienten in X- und Y-Richtung. Dann werden diejenigen Pixel unterdrückt, welche entlang der Gradientenrichtung kein - Maximum darstellen. Zum Abschluss wird das Bild mit einem Hysterese-Schwellwert binarisiert. Das bedeutet, dass alle Pixel über einem + entwickelten und in \cite{Canny:computationAlapproachEdgeDetection} veröffentlichten Algorithmus. Dieser bestimmt für jeden \gls{Pixel} den + Gradientenbetrag der Gradienten in X- und Y-Richtung. Dann werden diejenigen \gls{Pixel} unterdrückt, welche entlang der Gradientenrichtung kein + Maximum darstellen. Zum Abschluss wird das Bild mit einem Hysterese-Schwellwert binarisiert. Das bedeutet, dass alle \gls{Pixel} über einem initialen, oberen Schwellwert als Kanten gesetzt werden und mittels eines zweiten, niedrigeren Schwellwerte, Lücken zwischen diesen Pixeln geschlossen werden. \cite{Nischwitz:Computergrafik2} @@ -78,7 +78,7 @@ Wird dieser Code auf das Beispielbild \ref{fig: beispiel bild} angewendet und das Ergebnis des \glspl{canny} ausgegeben, ergibt sich \autoref{fig: canny edges}. Im Gegensatz zu alternativen, wie einer reinen Grabentenbetrachtung, liefert der \gls{canny} - Kantenmarkierungen (hier in weiß) die nur ein\todo{einen?} Pixel breit sind. Dies ermöglicht die in den folgenden Unterkapiteln + Kantenmarkierungen (hier in weiß) die nur ein\todo{einen?} \gls{Pixel} breit sind. Dies ermöglicht die in den folgenden Unterkapiteln beschriebenen Schritte. \begin{figure} @@ -127,8 +127,8 @@ \end{table} Um die Klassifizierung in \gls{python} durchzuführen, wird zuerst ein weiteres, leere 8-Bit Bild mit identischer Größe angelegt. Dann wird - erneut über alle Pixel des Bildes iteriert. Da allerdings die meisten Pixel schwarz und damit uninteressant sind, können diese direkt - verworfen werden. Für alle verbleibenden, weißen Pixel wird die Klassifizierung durchgeführt. + erneut über alle \gls{Pixel} des Bildes iteriert. Da allerdings die meisten \gls{Pixel} schwarz und damit uninteressant sind, können diese direkt + verworfen werden. Für alle verbleibenden, weißen \gls{Pixel} wird die Klassifizierung durchgeführt. \begin{lstlisting}[ float, @@ -173,10 +173,10 @@ \end{split} \end{equation} - Zuerst die Gradienten $d_x$ und $d_y$ ermittel. Dazu wird die $3\!\times\!3$ Pixelnachbarschaft des aktuellen Pixels elementweise mit dem + Zuerst die Gradienten $d_x$ und $d_y$ ermittel. Dazu wird die $3\!\times\!3$ \gls{Pixelnachbarschaft} des aktuellen Pixels elementweise mit dem jeweiligen Sobel-\gls{Kernel} multipliziert und die Summe der Ergebnismatrix gebildet (siehe \autoref{eq: dx dy}). Das Pythonpaket \lstinline{numpy} stellt hierfür sehr hilfreiche Funktion zum Arbeiten mit Matrizen zu Verfügung. Dadurch lässt sich diese Operation in - wenigen Zeilen durchführen, wie \autoref{code dx dy} gezeigt. + wenigen Zeilen durchführen, wie \autoref{code: dx dy} gezeigt. \begin{lstlisting}[ float, @@ -268,14 +268,14 @@ Fahrspurmarkierung. Für den ersten Schritt ist es ein weiters mal nötig über das gesamte Bild zu iteriert. Auch diesmal können wieder alle schwarzen Pixel - übersprungen werden. Wird ein klassifiziertes Pixel gefunden, muss überprüft werden, ob es sich um ein Startpixel handelt. Startpixel sind - Pixel, die keine Nachbarn in der ihrer Klasse entsprechenden Ursprungsrichtung haben. Zum Beispiel wäre ein Pixel der \emph{Vertikal} - Klasse ein Startpixel, wenn sich direkt oder diagonal über ihm keine weiteren Pixel derselben Klasse befinden. + übersprungen werden. Wird ein klassifiziertes \gls{Pixel} gefunden, muss überprüft werden, ob es sich um ein Startpixel handelt. Startpixel sind + Pixel, die keine Nachbarn in der ihrer Klasse entsprechenden Ursprungsrichtung haben. Zum Beispiel wäre ein \gls{Pixel} der \emph{Vertikal} + Klasse ein Startpixel, wenn sich direkt oder diagonal über ihm keine weiteren \gls{Pixel} derselben Klasse befinden. Ist ein Startpixel gefunden, wird es für später abgespeichert. Nun wird der Linie zu ihrem Ende gefolgt. Dazu wird der nächste Nachbarpixel gesucht. Da die grobe Richtung entsprechend der Klasse bekannt ist, müssen hier nicht alle Nachbarpixel überprüft werden. - Beim Beispiel mit der \emph{Vertikal} Klasse müssten die Pixel direkt und diagonal unterhalb betrachtet werden. Existiert ein Nachbar wird - dieser ausgewählt und der Prozess wiederholt. Dabei werden alle bereits besuchten Pixel aus dem Bild gelöscht, damit sich nicht nocheinaml + Beim Beispiel mit der \emph{Vertikal} Klasse müssten die \gls{Pixel} direkt und diagonal unterhalb betrachtet werden. Existiert ein Nachbar wird + dieser ausgewählt und der Prozess wiederholt. Dabei werden alle bereits besuchten \gls{Pixel} aus dem Bild gelöscht, damit sich nicht nocheinaml untersucht werden. \begin{lstlisting}[ @@ -302,7 +302,7 @@ lines.append(l) \end{lstlisting} - Hat ein Pixel keine weiteren Nachbarn, ist er der Endpunkt dieser Linie. Start und Endpunkt werden in ein Linienobjekt zusammengefasst + Hat ein \gls{Pixel} keine weiteren Nachbarn, ist er der Endpunkt dieser Linie. Start und Endpunkt werden in ein Linienobjekt zusammengefasst und abgespeichert. Zusätzliche wird ebenfalls die Orientierungsklasse mit abgespeichert. Mittels Start- und Endpunkt kann außerdem die Länge der Linie bestimmt werden. Da durch die in \autoref{sub: genauigkeit klassifizierung} @@ -430,7 +430,7 @@ die For-Schleifen für beide Dimensionen des Bildes, welche explizit einzeln verwendet werden müssen, und die Beachtung von Datentypen. Die Erstellung des benötigten, leeren Bildes und die For-Schleifen sind in \autoref{code: schleifen klassifizierung} zu sehen. Auch hier - werden wieder aller leeren Pixel übersprungen. + werden wieder aller leeren \gls{Pixel} übersprungen. \begin{lstlisting}[ float, @@ -447,7 +447,7 @@ \end{lstlisting} Die Bestimmung der Gradienten mittels Sobel wirkt in \gls{C++} deutlich komplizierter, da hier vieles manuell gemacht werden muss, was in - \gls{python} von \lstinline{numpy} erledigt wurde. Mit zwei For-Schleifen wird über die $3\!\times\!2$ Pixelnachbarschaft iteriert, die + \gls{python} von \lstinline{numpy} erledigt wurde. Mit zwei For-Schleifen wird über die $3\!\times\!2$ \gls{Pixelnachbarschaft} iteriert, die Elemente mit dem \gls{Kernel} multipliziert und aufsummiert, wie in \autoref{code: sobel c++} zu sehen. \begin{lstlisting}[ @@ -505,12 +505,12 @@ \subsubsection{Linienbildung} Mit dem klassifizierten Bild kann nun dieselbe Methodik zur Identifizierung zusammenhängender Linien wie in Python angewendet werden. - Allerdings ist in \gls{C++} das Definieren und Testen der relevanten Pixelnachbarschaft nicht so übersichtlich möglich wie in Python. + Allerdings ist in \gls{C++} das Definieren und Testen der relevanten \gls{Pixelnachbarschaft} nicht so übersichtlich möglich wie in Python. Daher müssen viele lange \lstinline{if} Bedingungen verwendet werden, welche in den folgenden Codebeispielen zur Übersichtlichkeit verkürzt sind. Begonnen wird wieder mit einer doppelten For-Schleife über das gesamte Bild, wie in \autoref{code: loop for lines c++} zu sehen. Dabei - werden wieder alle leeren Pixel vernachlässigt. + werden wieder alle leeren \gls{Pixel} vernachlässigt. \begin{lstlisting}[ float, @@ -526,14 +526,14 @@ continue; \end{lstlisting} - Für jeden Pixel wird wieder überprüft, ob er ein Startpixel ist. Genau wie in \gls{python} ist hierfür wieder der Klasse entsprechend eine - Pixelnachbarschaft relevant. Ist dort ein Nachbar gleicher Klasse vorhanden, wird mit dem nächsten Pixel weitergemacht. Dies ist in + Für jeden \gls{Pixel} wird wieder überprüft, ob er ein Startpixel ist. Genau wie in \gls{python} ist hierfür wieder der Klasse entsprechend eine + \gls{Pixelnachbarschaft} relevant. Ist dort ein Nachbar gleicher Klasse vorhanden, wird mit dem nächsten \gls{Pixel} weitergemacht. Dies ist in \autoref{code: test start c++} gezeigt. \begin{lstlisting}[ float, style=example, - caption={Überprüfen, ob ein Pixel ein Startpixel ist}, + caption={Überprüfen, ob ein \gls{Pixel} ein Startpixel ist}, label=code: test start c++, language=C++ ] @@ -559,7 +559,7 @@ \end{lstlisting} Ist ein Startpixel gefunden, wird er gespeichert und wieder so lange der nächste Nachbar ausgewählt, bis kein Nachbar mehr vorhanden ist. - Dann ist die gesamte Linie nachverfolgt. Dabei werden alle besuchten Pixel aus dem Bild gelöscht. Siehe dazu \autoref{code: follow c++}. + Dann ist die gesamte Linie nachverfolgt. Dabei werden alle besuchten \gls{Pixel} aus dem Bild gelöscht. Siehe dazu \autoref{code: follow c++}. \begin{lstlisting}[ float, diff --git a/chap/kalibrierung.tex b/chap/kalibrierung.tex index a5a358d561f73205d698c6c2b75812b0bd9e70c4..3b9a3d43c6727d72683586516eea579332d11eed 100644 --- a/chap/kalibrierung.tex +++ b/chap/kalibrierung.tex @@ -222,10 +222,10 @@ Um zu zeigen, wie sich das Bild damit verbessern lässt, werden die Ergebnisse auf eines der Bilder angewandt. Da sich die Abmessungen des entzerrten Bildes von denen des Verzehrten unterscheiden, wird zuerst die \gls{OpenCV} Funktion \lstinline{getOptimalNewCameraMatrix()} verwendet, welche eine weiter Skalierte Kameramatrix ermittelt, mit der die Abmessungen zueinander passen. Diese liefert außerdem eine - \gls{ROI}, also den Bildbereich der nur relevante (nicht leere) Pixel enthält. + \gls{ROI}, also den Bildbereich der nur relevante (nicht leere) \gls{Pixel} enthält. Mit dieser zusätzlichen Matrix kann nun die \gls{OpenCV} Funktion \lstinline{undistort()} auf das Bild angewandt werden. Diese Produziert das - entzerrte Bild mit leeren Pixeln in den Bereichen, wo keine Informationen im Originalbild vorlagen. Um diese leeren Pixel zu entfernen wird + entzerrte Bild mit leeren Pixeln in den Bereichen, wo keine Informationen im Originalbild vorlagen. Um diese leeren \gls{Pixel} zu entfernen wird das Bild auf die \gls{ROI} reduziert. \medskip @@ -322,16 +322,16 @@ ); \end{lstlisting} - Mit diesen Werten können nun \emph{Mappings} erzeugt werden, welche die geometrische Beziehung zwischen einem Pixel im Originalbild und - einem Pixel im entzerrten Bild abspeichern. Es werden zwei \emph{Mappings} für die X und die Y-Koordinate erzeugt, welche in globalen + Mit diesen Werten können nun \emph{Mappings} erzeugt werden, welche die geometrische Beziehung zwischen einem \gls{Pixel} im Originalbild und + einem \gls{Pixel} im entzerrten Bild abspeichern. Es werden zwei \emph{Mappings} für die X und die Y-Koordinate erzeugt, welche in globalen Variablen abgelegt werden. Das ist notwendig damit die Informationen der \gls{Callback} zur Verfügung stehen. Zuvor ist es aber noch sinnvoll, eine umskalierte, optimierte Kameramatrix zu erzeugen. \gls{OpenCV} stellt hierzu die Funktion \lstinline{getOptimalNewCameraMatrix()} zur Verfügung. Diese erstellt die neue Matrix abhängig von einem freien Skalierungsparameter - $\alpha$. Für $\alpha=0$ ist die zurückgegebene Matrix so gewählt, dass das entzerrte Bild möglichst wenig unbekannte Pixel enthält. Das - bedeutet aber, dass einige Pixel des Originalbildes außerhalb des neuen Bildbereiches liegen und vernachlässigt werden. Mit $\alpha=1$ - enthält das entzerrte Bild alle Pixel des Originalbildes, allerdings bleiben einige Pixel schwarz. Da die Funktion zusätzlichen eine - \gls{ROI} liefert, welches den Bildausschnitt ohne schwarze Pixel beschreibt, wird hier $\alpha=1$ verwendet. Die veröffentlichten Bilder + $\alpha$. Für $\alpha=0$ ist die zurückgegebene Matrix so gewählt, dass das entzerrte Bild möglichst wenig unbekannte \gls{Pixel} enthält. Das + bedeutet aber, dass einige \gls{Pixel} des Originalbildes außerhalb des neuen Bildbereiches liegen und vernachlässigt werden. Mit $\alpha=1$ + enthält das entzerrte Bild alle \gls{Pixel} des Originalbildes, allerdings bleiben einige \gls{Pixel} schwarz. Da die Funktion zusätzlichen eine + \gls{ROI} liefert, welches den Bildausschnitt ohne schwarze \gls{Pixel} beschreibt, wird hier $\alpha=1$ verwendet. Die veröffentlichten Bilder werden zwar auf die \gls{ROI} reduziert, aber die vorhanden Informationen werdenden grundsätzlich erhalten und bei Bedarf kann das Programm einfach angepasst werden, um die vollständigen Bilder zu veröffentlichen. @@ -360,7 +360,7 @@ \lstinline{toCvCopy()} kopiert die Daten des Originalbildes in eine OpenCV Matrix, welche weiter verwendet werden kann. Das Bild kann nun mit der OpenCV Funktion \lstinline{remap()} entzerrt werden. Diese benutzt die zuvor bestimmten \emph{Mappings}, um - jeden Pixel des Originalbildes an die korrekte Position im entzerrten Bild zu übertragen. Dabei wird linear interpoliert. + jeden \gls{Pixel} des Originalbildes an die korrekte Position im entzerrten Bild zu übertragen. Dabei wird linear interpoliert. Das Erhalten Bild wird auf die \gls{ROI} reduziert und unter dem \gls{Topic} \lstinline{/img/color} veröffentlicht. Außerdem wird ein Schwarz-Weiß Version erzeugt und diese als \lstinline{/img/gray} veröffentlicht (was hier aber nicht gezeigt ist). @@ -434,12 +434,12 @@ \end{table} - \section{Extrinsische Kalibrierung} \label{sec: extrensic} + % \section{Extrinsische Kalibrierung} \label{sec: extrensic} - \begin{figure} - \includegraphics[scale=.5]{svg/Sketch_JetBot_translation.pdf} - \end{figure} + % \begin{figure} + % \includegraphics[scale=.5]{svg/Sketch_JetBot_translation.pdf} + % \end{figure} - \begin{figure} - \includegraphics[scale=.5]{svg/Sketch_JetBot_front.pdf} - \end{figure} + % \begin{figure} + % \includegraphics[scale=.5]{svg/Sketch_JetBot_front.pdf} + % \end{figure}