diff --git a/.vscode/ltex.dictionary.de-DE.txt b/.vscode/ltex.dictionary.de-DE.txt
index 721102105a62782e58304259c27e29c6a98c4504..d9df59545ad706c098a9e325c76d241c93339856 100644
--- a/.vscode/ltex.dictionary.de-DE.txt
+++ b/.vscode/ltex.dictionary.de-DE.txt
@@ -51,3 +51,8 @@ if
 For-Schleife
 Debuggmöglichkeiten
 Hough
+Klassifizung
+Gradientenwinkel
+Klassifizierungsschritt
+fehlklassifizierte
+Headerdatei
diff --git a/.vscode/ltex.hiddenFalsePositives.de-DE.txt b/.vscode/ltex.hiddenFalsePositives.de-DE.txt
index e26a6e952aada689cd032359150e38f645ccd86a..4b7fb4d6e668eb1bfd40d681fda3cd358892787e 100644
--- a/.vscode/ltex.hiddenFalsePositives.de-DE.txt
+++ b/.vscode/ltex.hiddenFalsePositives.de-DE.txt
@@ -22,3 +22,6 @@
 {"rule":"GERMAN_SPELLER_RULE","sentence":"^\\QJede \\E(?:Dummy|Ina|Jimmy-)[0-9]+\\Q kann eigene Informationen als sogenannte Dummies veröffentlichen und andere, parallel laufende Dummies können diese abonnieren.\\E$"}
 {"rule":"DOPPELTES_AUSRUFEZEICHEN","sentence":"^\\QROS's ??\\E$"}
 {"rule":"GERMAN_SPELLER_RULE","sentence":"^\\QDieses kann unter dem \\E(?:Dummy|Ina|Jimmy-)[0-9]+\\Q /img/temp abgerufen und live angeschaut werden.\\E$"}
+{"rule":"GERMAN_SPELLER_RULE","sentence":"^\\QOptimierung durch eigene Implementierung des Canny-Edge-Detectors.\\E$"}
+{"rule":"GERMAN_SPELLER_RULE","sentence":"^\\QDa dieser Code sehr viele verschachtelte und gedoppelte if-Abfragen aufweist, wird er in \\E(?:Dummy|Ina|Jimmy-)[0-9]+\\Q vereinfach gezeigt.\\E$"}
+{"rule":"GERMAN_SPELLER_RULE","sentence":"^\\QDer Unterschied liegt in der Funktion edgeDetectionClassification().\\E$"}
diff --git a/Bachelorarbeit.pdf b/Bachelorarbeit.pdf
index 402464fbfce5b54eba19f02f9e592dc4aca61577..723a10e9b51cac91bb82ace374e64d8c34c38e2c 100644
Binary files a/Bachelorarbeit.pdf and b/Bachelorarbeit.pdf differ
diff --git a/chap/ausblick.tex b/chap/ausblick.tex
index ba3eca63f4626eea8c22750547d26a2c381f598f..55c7536c8bb64ae75605b1158a6663c58ae37ae7 100644
--- a/chap/ausblick.tex
+++ b/chap/ausblick.tex
@@ -4,6 +4,7 @@
 	des Programmes noch Weiterentwicklungsmöglichkeiten. Dadruch könnte die Erfassungsgenauigkeit insbesondere in schwierigen Situationen erhöt und
 	weite er Informationen gewonnen werden.
 
+
 	\subsubsection{Mehr Orientierungsklassen}
 
 		Derzeit wird bei der Klassifizierung der Kantenpixel lediglich in vier unterschiedliche Klassen eingeteilt. Das kann insbesondere bei Kurven
@@ -13,6 +14,7 @@
 		Durch mehr Klassen und Berücksichtigung von benachbarten Klassen in der Linienverfolgung ließen sich potenziell auch gekrümmte Linien
 		verfolgen.
 
+
 	\subsubsection{Reduzierung von Falschklassifizierungen}
 
 		Dies ist möglicherweise bereits Teileweise durch den vorherigen Punkt mit abgedeckt, bedarf aber trotzdem einer weiteren Erklärung. Derzeit
@@ -24,8 +26,6 @@
 		Genauigkeit bei der Klassifizierung wäre ebenfalls denkbar.
 
 
-
-
 	\subsubsection{Datenübergabe an weiter Prozesse}
 
 		Da diese Arbeit als Grundlage für weitere Prozesse, z.B. zum autonomen Einhalten der Spur beim Fahren, müsste die genaue Methode zur
@@ -36,4 +36,8 @@
 		Diese müsste bei Bedarf eingerichtet werden.
 
 
-		\todo[inline]{noch was?}
+	\subsubsection{Weiters Optimierungspotenzial}
+
+		Der Versuch den \gls{C++} Quellcode durch eine eigene Implementierung des \gls{canny} zu optimieren, hat leider keine nutzbaren Ergebnisse
+		erbracht. Grundsätzlich sind die gemachten Überlegungen aber gültig und mit tieferem Verständnis der Programmiersprache wäre eine erfolgreiche
+		Optimierung möglich. Da \gls{OpenCV} Quelloffen ist, wäre es auch denkbar, deren Quellcode direkt zu verwenden.
diff --git a/chap/fazit.tex b/chap/fazit.tex
index 4179e97ceaa469f9da8be8145dca4b807c099e84..d002b5468a63945e81ab1fba4d05eedf5c6daa3a 100644
--- a/chap/fazit.tex
+++ b/chap/fazit.tex
@@ -21,3 +21,6 @@
 
 	Zur Veranschaulichung wurden die Konturen in ein leeres Bild eingezeichnet und dieses Ebenfalls veröffentlicht. Dadruch ist ein direkter Vergleich
 	zwischen Originalbild und gefunden Konturen in Echtzeit möglich.
+
+	Eine weitere Optimierung durch eine eigene Implementierung des \gls{canny} und Kombinieren von diesem mit dem Klassifizierungsschritt, war nicht
+	möglich\todo{Stattdessen erfolgreich ?}. Ziertuch wurden deutlich schlechtere Laufzeiten erzielt.
diff --git a/chap/implementation.tex b/chap/implementation.tex
index 9e25ab0ac8ac6e6f743889f2d4ab6608e5c30e63..ba5765bc9fdf02093c37ed89c8ec06ca7186610e 100644
--- a/chap/implementation.tex
+++ b/chap/implementation.tex
@@ -397,6 +397,8 @@
 		bereits auf einem leistungsfähigen Entwickler-PC Laufzeiten von $>0,25\,\s$ hat, wird diese im \gls{C++} implementiert. Dies wird die
 		Performance deutlich verbessern.
 
+		\subsection{Erstellung des Quellcodes}
+
 		Die Beziehung der neuen \gls{ROS Node} zu den bestehenden \glspl{ROS Node} wurde bereits in \autoref{fig: topics marker detection} skizziert.
 		Dort sieht man, dass die \gls{ROS Node} wir mit dem Namen \lstinline{lane_marker_detection} inizialisiert wird. Außerdem wird das \gls{Topic}
 		\lstinline{/img/gray} von der Entzerrer-\gls{ROS Node} abonniert, um jedes Schwarz-Weiß Bild zu bekommen. Das Bild mit den eingezeichneten,
@@ -678,6 +680,166 @@
 			\end{table}
 
 
-	% \section{Optimierung durch eigene Implementierung des Canny-Edge-Detectors}
+	\section{Optimierung\todo{oder "Versuchte Optimierung" ??} durch eigene Implementierung des Canny-Edge-Detectors}
+
+		Im Folgenden wird untersucht, ob eine eigene Implementierung des \gls{canny} einen Vorteil gegenüber der Verwendung von \gls{OpenCV} bietet.
+		Da die Bibliotheksfunktionen viel potenziell nicht benötigte Zusatzfunktionen und Schutzmechanismen enthalten, welche Zeit
+		und Performance benötigen und für deren Verwendung eine Datenumwandlung notwendig ist, kann eine eigene Implementierung hier Vorteile bieten.
+
+		Außerdem führt der \gls{canny} intern bereits eine Sobel-Filterung durch und es ließe sich die Klassifizierung dort direkt mit durchführen.
+		Das eliminiert die Notwendigkeit zweimal über das Bild zu integrieren.
+
+
+		\subsection{Anpassung des Quellcodes}
+
+			Da ohne \gls{OpenCV} das Bild nicht mehr in eine praktische Matrixform umgewandelt wird, muss ein eigener Datentyp definiert werden.
+			Dieser speichert die Daten im selben Format wie der verwendete \gls{ROS} Datentyp, sodass keine Umwandlung notwendig ist. Der Datentyp
+			ermöglicht lediglich den Zugriff auf die Daten über $x$ und $y$ Koordinaten.
+
+			\autoref{code: image class} zeigt die Umsetzung mit dem Namen \lstinline{Image}. Initialisiert werden kann eine Instanz entweder nur mit
+			Höhe und Breite, wodurch ein neues, leeres Bild erzeugt wird, oder mit einem existieren Bild, wobei dessen Daten übernommen werden.
+
+			Um auf die Bilddaten zuzugreifen, werden die Operatoren \lstinline{[]} überladen, sodass ein Punkt bestehen aus einer $x$ und einer $y$
+			Koordinate übergeben werden kann. Das entsprechende Pixel wird dann zurückgegeben oder beschreiben.
+
+			\begin{lstlisting}[
+				float,
+				style=example,
+				caption={Eigener Datentyp zum Ablegen von Bildern},
+				label=code: image class,
+				language=C++
+			]
+				class Image {
+				   public:
+				    std::vector<uint8_t> data;
+				    int width; int height;
+
+				    Image(int width, int height): data(width*height, 0), width(width), height(height) {};
+				    Image(sensor_msgs::Image img): data(img.data), width(img.width), height(img.height) {};
+
+				    uint8_t& operator[](pnt const& p) { return data[p.y*width + p.x]; }
+				    const uint8_t& operator[](pnt const& p) const { return data[p.y*width + p.x]; }
+				};
+			\end{lstlisting}
+
+			Die \gls{Callback} \lstinline{callback_image()} verläuft völlig analog zur Implementierung mit \gls{OpenCV}. Der Unterschied liegt in der
+			Funktion \lstinline{edgeDetectionClassification()}. Diese führt nun die drei Schritte des \gls{canny} durch: Gradienten Bestimmung,
+			Unterdrücken von Nicht-Lokalen-Maxima und Hysterese-Grenzwertbildung (siehe \cite{Canny:computationAlapproachEdgeDetection}).
+
+			Zusätzlich wird außerdem die Klassifizierung durchgeführt. Da im ersten Schritt die Sobel-Gradienten ohnehin berechnet werden, können hier
+			ohne viel Zusatzaufwand auch die Gradientenwinkel mit bestimmt werden. Der \autoref{code: own canny 1} zeigt diesen Schritt.
 
-	% 	\subsection{Performance Betrachtung}
+			\begin{lstlisting}[
+				float,
+				style=example,
+				caption={Bestimmung der Sobel-Gradienten im \gls{canny}},
+				label=code: own canny 1,
+				language=C++
+			]
+				// Compute gradient magnitude and orientation
+				for (int u=1; u < image.height-1; u++) {
+					for (int v=1; v < image.width-1; v++) {
+						int dx=0, dy=0;
+						for (int y=0; y<3; y++){
+							for (int x=0; x<3; x++){
+								dx += SOBEL_X[y*3+x] * image[pnt(v+y-1,u+x-1)];
+								dy += SOBEL_Y[y*3+x] * image[pnt(v+y-1,u+x-1)];
+							}
+						}
+						gards[pnt(v,u)] = static_cast<int>(sqrt(dx*dx+dy*dy));
+						agls[pnt(v,u)] = static_cast<float>(atan2(dy,dx) / 3.1415*180.0);
+					}
+				}
+			\end{lstlisting}
+
+			Im zweiten Schritt werden nun zusätzlich zur Unterdrückung von Pixeln, welche kein lokales Maximum sind, auch die Klassen bestimmt und
+			abgespeichert. Da dieser Code sehr viele verschachtelte und gedoppelte \lstinline{if}-Abfragen aufweist, wird er in
+			\autoref{code: own canny 2} vereinfach gezeigt.
+
+
+			\begin{lstlisting}[
+				float,
+				style=example,
+				caption={Klassifizierung und Unterdrückung von Nicht-Lokalen-Maxima},
+				label=code: own canny 2,
+				language=C++
+			]
+				// Non-maximum suppression and classification
+				for (int u=1; u < image.height-1; u++) {
+					for (int v=1; v < image.width-1; v++) {
+						int grad = gradients[pnt(v,u)];
+						int arc = angles[pnt(v,u)];
+
+						if (arc < 0)
+							uint8_t negative = 0x10;
+						arc = fabsf(arc);
+
+						if ( /* is in a specific angle range */ ) {
+							canny_edges[pnt(v,u)] = negative|/* CLASSIFICATION */;
+							if ( /* is not local maximum */)
+								gradients[pnt(v,u)] = 0;
+						}
+					}
+				}
+			\end{lstlisting}
+
+			Der letzte Schritt der Hysterese-Grenzwertbildung wurde für diese Anwendung zu einem simplen Grenzwert vereinfacht. Dies erzeugt
+			ausreichend gute Ergebnisse.
+
+			Wichtig ist außerdem, dass im Gegensatz zu einem herkömmlichen \gls{canny} kein Binarisiertes Bild, mit nur völlig weißen oder völlig
+			schwarzen Pixelwerten, zurückgegeben wird. Stadtmessen enthalten alle detektierten Kantenpixel bereits den Wert, der ihre Klasse
+			repräsentiert.
+
+			\begin{lstlisting}[
+				float,
+				style=example,
+				caption={Grenzwertbildung und Ergebnissrückgabe},
+				label=code: own canny 3,
+				language=C++
+			]
+				// thresholding
+				uint8_t T1 = 40;
+				for (int u=1; u < image.height-1; u++)
+					for (int v=1; v < image.width-1; v++) {
+						pnt p(v,u);
+						canny_edges[p] = gradients[p] < T1 ? 0 : canny_edges[p];
+					}
+				return canny_edges;
+			\end{lstlisting}
+
+
+		\subsection{Performance Betrachtung}
+
+			Die wichtigste Frage ist nun, wie sich diese Implementierung im Gegensatz zum Ansatz mit \gls{OpenCV} verhält. Dazu wurden wieder die
+			CPU-Auslastung mittels \lstinline{jtop} erfasst und die Durchlaufzeit des Algorithmus für jedes Bild gemessen.
+
+			\begin{figure}
+				\includegraphics[width=.6\textwidth, trim={0 0 12px 31px}, clip]{img/jtop_cameraUndistortDetection_own.png}
+				\caption{CPU Auslastung des JetBots mit laufender Kamera, Entzerrung und Markierungserkennung mit eigener Implementierung}
+				\label{fig: jtop markings own}
+			\end{figure}
+
+			Wie aus \autoref{fig: jtop markings own} abzulesen ist, ist die CPU-Auslastung mit durchschnittlichen $37.75\,\percent$ exakt identisch
+			zur Implementierung mit \gls{OpenCV}. Das ist wenig überraschen, da der Algorithmus Grunde immer noch dieselbe Arbeit verrichtet.
+
+			\pagebreak[3]
+			Die Messung der Laufzeit zeigt allerdings, dass die eigene Implementierung deutlich weniger performant ist. Mit einer Durchlaufzeit von
+			$\approx 22,58\,\ms$ ist diese Version um mehr als Faktor 6 größer. Hier gleichen die Vorteile durch das Kombinieren von Einzelschritten
+			die Leistungsfähigkeit einer stark optimierten Bibliotheksfunktion also leider nicht mal annähern aus.
+
+			\begin{table}
+				\caption{Gemessene Laufzeit bei 10 Durchläufen der \gls{Callback}}
+				\begin{tabular}{r|S}
+					Durchlauf Nr. & \multicolumn{1}{c}{gemessene Laufzeit} \\\hline
+					1             & 22,266302 \,\ms                        \\
+					2             & 22,063851 \,\ms                        \\
+					3             & 22,126713 \,\ms                        \\
+					4             & 22,595543 \,\ms                        \\
+					5             & 23,369521 \,\ms                        \\
+					6             & 22,278288 \,\ms                        \\
+					7             & 24,207147 \,\ms                        \\
+					8             & 22,546738 \,\ms                        \\
+					9             & 22,096768 \,\ms                        \\
+					10            & 22,201099 \,\ms                        \\
+				\end{tabular}
+			\end{table}
diff --git a/img/jtop_cameraUndistortDetection_own.png b/img/jtop_cameraUndistortDetection_own.png
new file mode 100644
index 0000000000000000000000000000000000000000..5c4274c5366c803a8d58a9bf42e3c4c8b5ae32bc
Binary files /dev/null and b/img/jtop_cameraUndistortDetection_own.png differ