Skip to content
Snippets Groups Projects
Commit c0b1f233 authored by Jan Wille's avatar Jan Wille
Browse files

klassifizierung

parent 767c002b
No related branches found
No related tags found
No related merge requests found
...@@ -39,3 +39,6 @@ jtop ...@@ -39,3 +39,6 @@ jtop
Debug-Nachricht Debug-Nachricht
Operating Operating
JetBot JetBot
Canny-Edge-Detektor
Gradientenbetrag
Gradientenrichtung
...@@ -11,3 +11,4 @@ ...@@ -11,3 +11,4 @@
{"rule":"GERMAN_SPELLER_RULE","sentence":"^\\QBeim Start der \\E(?:Dummy|Ina|Jimmy-)[0-9]+\\Q wird die main() Funktion aufgerufen, welche die notwendigen \\E(?:Dummy|Ina|Jimmy-)[0-9]+\\Q Funktionen zur Initialisierung aufruft, das benötigt \\E(?:Dummy|Ina|Jimmy-)[0-9]+\\Q abonniert, ein \\E(?:Dummy|Ina|Jimmy-)[0-9]+\\Q anhängt und die eigenen Dummies veröffentlicht.\\E$"} {"rule":"GERMAN_SPELLER_RULE","sentence":"^\\QBeim Start der \\E(?:Dummy|Ina|Jimmy-)[0-9]+\\Q wird die main() Funktion aufgerufen, welche die notwendigen \\E(?:Dummy|Ina|Jimmy-)[0-9]+\\Q Funktionen zur Initialisierung aufruft, das benötigt \\E(?:Dummy|Ina|Jimmy-)[0-9]+\\Q abonniert, ein \\E(?:Dummy|Ina|Jimmy-)[0-9]+\\Q anhängt und die eigenen Dummies veröffentlicht.\\E$"}
{"rule":"GERMAN_SPELLER_RULE","sentence":"^\\QDie Beziehung der Dummies ist in \\E(?:Dummy|Ina|Jimmy-)[0-9]+\\Q Grafisch dargestellt.\\E$"} {"rule":"GERMAN_SPELLER_RULE","sentence":"^\\QDie Beziehung der Dummies ist in \\E(?:Dummy|Ina|Jimmy-)[0-9]+\\Q Grafisch dargestellt.\\E$"}
{"rule":"GERMAN_SPELLER_RULE","sentence":"^\\QUm die Laufzeit der \\E(?:Dummy|Ina|Jimmy-)[0-9]+\\Q zu bestimmen wird die aktuelle Zeit wie sie von der Funktion ros::Time::now() zurückgegeben wird verwendet.\\E$"} {"rule":"GERMAN_SPELLER_RULE","sentence":"^\\QUm die Laufzeit der \\E(?:Dummy|Ina|Jimmy-)[0-9]+\\Q zu bestimmen wird die aktuelle Zeit wie sie von der Funktion ros::Time::now() zurückgegeben wird verwendet.\\E$"}
{"rule":"DE_CASE","sentence":"^\\QUm die Datenmenge gering und die Laufzeit schnell zu halten, werden lediglich die vier Klassen Vertikal, Horizontal, Diagonal 1 und Diagonal 2 verwendend.\\E$"}
No preview for this file type
...@@ -74,6 +74,14 @@ ...@@ -74,6 +74,14 @@
} }
} }
\newglossaryentry{JetBot}{
name={JetBot},
description={
Der in deser Arbeite verwendete Roboter. Gesteuert wird er von einem NVIDIA Jetson Nano Entwicklerboard, auf welchem die hier erstellten
Programme laufen. Weiteres ist in \autoref{sec: JetBot} beschrieben.
}
}
\newglossaryentry{Welt-coords}{ \newglossaryentry{Welt-coords}{
name={Weltkoordinaten}, name={Weltkoordinaten},
description={ description={
...@@ -100,12 +108,33 @@ ...@@ -100,12 +108,33 @@
name={ROI, kurz für Region of Interest}, name={ROI, kurz für Region of Interest},
text={ROI}, text={ROI},
description={ description={
Ein Bildbereich, der für die derzeitige Anwendung relevant ist. Das restlcihe Bild wird vernachlässigt. Ein Bildbereich, der für die derzeitige Anwendung relevant ist. Das restliche Bild wird vernachlässigt.
}
}
\newglossaryentry{gauss-filter}{
name={Gaussscher Filter},
description={
Eine Filterfunktion um Bilder zu glätten. Sie biltet den mittels einer 2D-Gaußfunktion gewichten Mittelwert einer Nachbarschschaft.
} }
} }
\newglossaryentry{canny}{
name={Canny-Edge-Detektor},
description={
Ein Algorithmus zur Erkennung von Kanten in Bildern.
}
}
\newglossaryentry{Gradientenorientierung}{
name={Gradientenorientierung},
description={
Richtung des Farbgradienten in einer Pixelnachbarschaft. Verläuft von dunklen Bildbereichen zu hellen Bildbereichen.
}
}
\newglossaryentry{Kernel}{ \newglossaryentry{Kernel}{
name={Kernel}, name={Kernel},
plural={Kerneln},
description={\todo{Beschreibung}} description={\todo{Beschreibung}}
} }
\chapter{Fahrspurerkennung} \label{chap: implementation} \chapter{Fahrspurerkennung} \label{chap: implementation}
\section{Bild Vorbereitung} \label{sec: bild vorbereitung} Dieses Kapitel thematisiert, wie die Erkennung der Fahrspurmarkierungen umgesetzt wir. Begonnen wird mit einer konzeptionellen Version in
Python, mit der der Ablauf des Algorithmus geplant und getestet wird. Danach wird die Logik in einer \gls{C++} \gls{ROS Node} umgesetzt, um die
best möglichst Performance zu erhalten.
\section{Canny-Edge-Detector} \label{sec: canny edge} \begin{figure}
\includegraphics[page=1,scale=.85]{svg/Topics_makerDetection.pdf}
\caption{Zusammenhang der Fahrspurmarkierung-Erkennungs \gls{ROS Node} mit den bestehenden \glspl{ROS Node}}
\label{fig: topics marker detection}
\end{figure}
Wie diese neuen \glspl{ROS Node} mit den bestehenden \glspl{ROS Node} in Beziehung stehen soll, ist in \autoref{fig: topics marker detection}
grafisch dargestellt. Neu ist dabei, dass diese \gls{ROS Node} das korrigierte Schwarz-Weiß Bild von der in \autoref{sec: undistort Node}
beschriebenen entzerrer \gls{ROS Node} abonniert und die eigenen Ergebnis als neues \gls{Topic} zur Verfügung stellt.
\section{Konzeptionierung in Python}
Die Entwicklung und Konzeptionierung des Algorithmus Erfolg in \gls{python}, da diese Sprache nicht kompiliert werden muss, was das Testen
beschleunigt, und generell einfacher zu verwenden ist.
Der Algorithmus lässt sich in mehre Einzelschritte aufteilen und wird daher in den folgen Unterkapitel beschreiben. Zur Übersicht
ist aber der gesamte Ablauf in \autoref{fig: PAP} vereinfacht skizziert. Angefangen wird dort mit dem Erhalten des Bildes, womit sowohl
manuelles laden eines Beispielbildes, als auch das Erhalten des Bildes über ein \gls{Topic} gemeint ist.
\begin{figure}
\includegraphics[scale=.85]{svg/PAP_marker_erkennung.pdf}
\caption{Ablauf des Algorithmus zur Erkennung von Fahrspurmarkierungen}
\label{fig: PAP}
\end{figure}
Während einer Testfahrt des \glspl{JetBot} wurden von der entzerrer \gls{ROS Node} veröffentlichte Bilder abgespeichert, sodass sie zum
lokalen Testen zur Verfügung stehen. Diese wurden unter \cite{git:dataset-strassen} abgelegt. In \autoref{fig: beispiel bild} ist eines dieser
Bilder gezeigt, mit dem im Folgenden die Einzelschritte demonstriert werden.
\begin{figure}
\includegraphics[width=.6\textwidth]{img/Marks_original.png}
\caption{Ein Beispiel Bild an dem der Ablauf demonstriert wird}
\label{fig: beispiel bild}
\end{figure}
\pagebreak
\subsection{Kantenerkennung mittels Canny-Edge-Detektor}
Begonnen wird mit der Detektion von Kante im Bild. Dazu wird das Bild zuerst mit \gls{OpenCV} geladen. \todo{Absatz Ja/Nein?}
\section{Orientierungserfassung mittels Sobel} \label{sec: sobel} Um kleine Störungen im Bild, welche bestehende Kanten verzerren oder als falsche Kante erkannt werden könnten, zu reduzieren, wird das
Bild mit einem \glslink{gauss-filter}{Gaußschen Filter} geglättet. Es wird ein $3\!\times\!3$ \gls{Kernel} mit einer Normalverteilung von
$\sigma=1,5$ verwendet. \gls{OpenCV} stell hierzu die Funktion \lstinline{GaussianBlur()} zur Verfügung, der das geladene Bild, die
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
initialen, oberen Schwellwert als Kanten gesetzt werden und mittels eines zweiten, niedrigeren Schwellwerte, Lücken zwischen diesen Pixeln
geschlossen werden. \cite{Nischwitz:Computergrafik2}
Auch dieser Algorithmus ist in \gls{OpenCV} bereits implementiert und wird für den ersten Entwurf verwendet. Die Funktion bekommt das
geladene und geglättet Bild sowie die beiden Hysterese-Schwellwerte übergeben. Diese ist auch in \autoref{code: canny} gezeigt.
\begin{lstlisting}[
float,
style=example,
caption={Laden, glätten eines Bildes und durchführen der Kantenerkennung mit \gls{OpenCV}},
label=code: canny,
language=Python
]
# load image (should be gray, so convert)
img = cv2.imread("./image.png")
img = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
# edge detection
img = cv2.GaussianBlur(img, (3,3), 1.5)
canny = cv2.Canny(img, 180, 40)
\end{lstlisting}
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
beschriebenen Schritte.
\begin{figure}
\includegraphics[width=.6\textwidth]{img/Marks_cannyedges.png}
\caption{Vom Canny-Edge-Detector gefundene Kanten}
\label{fig: canny edges}
\end{figure}
\subsection{Klassifizierung der Kantenpixel}
Nur die Identifikation von Pixeln als Kantenpixeln reicht nicht aus, um bereits Linienmarkierungen zu erkennen. Uns Menschen fällt es zwar
Leicht in \autoref{fig: canny edges} die gesuchten Linien zu identifizieren, für den Algorithmus handelt es sich aber nur um eine
"zufällige"\todo{"zufällige" OK?} Ansammlung von weißen Pixeln. Es werden weiter Informationen benötigt.
Deshalb werden jedem Kantenpixel eine Klasse entsprechend seiner Orientierung zugeordnet. Um die Datenmenge gering und die Laufzeit
schnell zu halten, werden lediglich die vier Klassen \emph{Vertikal}, \emph{Horizontal}, \emph{Diagonal 1} und \emph{Diagonal 2}
verwendend. Zusätzlich wird noch die Richtungsinformation als Vorzeichen abgespeichert.
Die Klassifizierte erfolgt anhand der \gls{Gradientenorientierung} eines Pixels. Dazu werden mit $3\!\times\!3$ Sobel-\glspl{Kernel} die
Gradienten $d_x$ und $d_y$ bestimmt. Mit der \lstinline{atan2()} Funktion kann aus diesen beiden Größen der Winkel des Gradientenvektors
$\vec{G}$ berechnet werden. Mit diesem Winkel kann nun entsprechen der \autoref{fig: gadienten orientierung} die Klasse bestimmt werden.
Dabei ist zu beachten das $\vec{G}$ immer orthogonal auf der eigentlichen Kante steht, deshalb ist die Klasse \emph{Vertikal} auch auf der
links-rechts Achse der Abbildung zu finden.
\begin{figure} \begin{figure}
\includegraphics[width=.4\textwidth]{svg/CannyEdge_Orientation.pdf} \includegraphics[width=.4\textwidth]{svg/CannyEdge_Orientation.pdf}
\caption{Klassifizierung der Gradientenorientierung (nach \cite{Vorlesung:Homann})} \caption{Klassifizierung der Gradientenorientierung (nach \cite{Homann:VorlesungBildverarbeitung})}
\label{fig: gadienten orientierung} \label{fig: gadienten orientierung}
\end{figure} \end{figure}
\section{Linienbildung} \label{sec: linien finden} \pagebreak
Die Klassifizierung wird in einer 8-Bit Variable abgespeichert, da so ein normales Graustufen-Bild als Datenstruktur verwendet werden
kann. Jeder Klasse wird dabei ein Bit wie folgt zugeordnet:
\begin{table}
\caption{Zuordnung der Klassen zu Bits}
\begin{tabular}{r l}
Bit & Klasse \\
1 & \emph{Vertikal} \\
2 & \emph{Diagonal 1} \\
3 & \emph{Diagonal 2} \\
4 & \emph{Horizontal} \\
5 & Vorzeichen-Bit \\
\end{tabular}
\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.
\begin{lstlisting}[
float,
style=example,
caption={Schleife über das vom \gls{canny} gelieferte Bild},
language=Python
]
for (u, v), e in np.ndenumerate(canny[1:-1, 1:-1]):
if not e:
continue
u += 1
v += 1
\end{lstlisting}
\begin{equation}\label{eq: dx dy}
\begin{split}
d_x &=
\begin{bmatrix}
p_{\text{-}1\text{-}1} & p_{\text{-}10} & p_{\text{-}11} \\
p_{0\text{-}1} & p_{00} & p_{01} \\
p_{1\text{-}1} & p_{10} & p_{11} \\
\end{bmatrix}
\circ
\begin{bmatrix}
0 & 0 & 0 \\
-1 & 0 & +1 \\
0 & 0 & 0 \\
\end{bmatrix}
\\
d_y &=
\begin{bmatrix}
p_{\text{-}1\text{-}1} & p_{\text{-}10} & p_{\text{-}11} \\
p_{0\text{-}1} & p_{00} & p_{01} \\
p_{1\text{-}1} & p_{10} & p_{11} \\
\end{bmatrix}
\circ
\begin{bmatrix}
0 & -1 & 0 \\
0 & 0 & 0 \\
0 & +1 & 0 \\
\end{bmatrix}
\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
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.
\begin{lstlisting}[
float,
style=example,
caption={Bestimmung der Gradienten $d_x$ und $d_y$},
label=code: dx dy,
language=Python
]
nh = img[u-1:u+2, v-1:v+2]
dx = np.sum(nh * SOBEL_X)
dy = np.sum(nh * SOBEL_Y)
\end{lstlisting}
Mit diesen werden wird nun die \lstinline{atan2(dy,dx)} Funktion aufgerufen. Diese gibt einen Winkel in rad zurück, welcher zur besseren
Nachvollziehbarkeit in Grad umgerechnet wird.
Nun werden durch eine Folge von Bedingungen die Klasse des aktuellen Pixels bestimmt. Zuerst wird das Vorzeichen bestimmt und im 5. Bit
abgespeichert. Dies vereinfacht die folgenden Abfragen, da für die \emph{Vertikale} und \emph{Horizontale} Klasse der Betrag des Winkels
ausreicht.
Ist die Klasse bestimmt, wird das entsprechende Bit des Pixels gesetzt. Die Umsetzung in Python ist in
\autoref{code: python Klassifizierung} gezeigt.
\begin{lstlisting}[
float,
style=example,
caption={Durchführen der Klassifizierung mittel des bestimmten Winkels},
label=code: python Klassifizierung,
language=Python
]
arc = atan2(dy, dx) / pi * 180
if arc < 0:
pixel_info[u, v] |= 0x10
arc = abs(arc)
if arc >= 157.5 or 22.5 > arc:
pixel_info[u, v] |= V
elif 22.5 <= arc < 67.5:
pixel_info[u, v] |= D1 if not pixel_info[u, v] else D2
elif 67.5 <= arc < 112.5:
pixel_info[u, v] |= H
elif 112.5 <= arc < 157.5:
pixel_info[u, v] |= D2 if not pixel_info[u, v] else D1
\end{lstlisting}
Wurde jeder Kantenpixel klassifiziert, ist der Vorgang beendet. Zur Veranschaulichung wurde ein Bild erstellt, wo jeder Klasse und
Vorzeichen eine eindeutige Farbe zugeordnet ist. So ist genau zu erkennen, welche Kanten derselben Klasse zugeordnet wurden. Diese Bild
ist in \autoref{fig: classified edges} gezeigt.
\begin{figure}
\includegraphics[width=.6\textwidth]{img/Marks_classification.png}
\caption{
Klassifizierte Kanten mit farblicher Markierung der unterschiedlichen Klassen
(Farben sind nicht identisch mit \autoref{fig: gadienten orientierung})
}
\label{fig: classified edges}
\end{figure}
\subsubsection{Genauigkeit der Klassifizierung}
Die Klassifizierung erfolgt leider nicht immer völlig zuverlässig. Gut klassifizierte Kanten haben für die gesamte Länge der Linie
dieselbe Klasse erhalten wie es im vergrößerten Bildausschnitt \autoref{fig: gute kante} zu sehen ist. Im Gegensatz dazu haben
unzuverlässig klassifizierte Kanten mehrere Klassen in einem engen Bereich und wechseln häufig sogar mehrfach zwischen mehreren
Klassen, wie das Beispiel in \autoref{fig: schlechte kante} zeigt.
\begin{figure}
\subfigure[Beispiel guter Kantenklassifizierung]{
\label{fig: gute kante}
\includegraphics[scale=4]{img/Marks_classification_cut1.png}
}
\subfigure[Beispiel schlechter Kantenklassifizierung]{
\label{fig: schlechte kante}
\includegraphics[scale=4]{img/Marks_classification_cut2.png}
}
\caption{Vergleich gut und schlecht klassifizierte Bildbereiche}
\end{figure}
Durch Störungen und generell schlechtere Bildqualität in weiter von der Kamera entfernten Bildbereichen weisen vor allem die äußeren
Linien viele dieser Ungenauigkeiten auf. Das führt dazu, das die nachfolgende Logik viele einzelne, kleine Linien anstatt der
vollständigen, durchgängigen Linie, erkennt.
Die zentralen Linienmarkierungen der eigenen Spur werden aber zuverlässig genug klassifiziert.
\subsection{Linienbildung} \label{sec: linien finden}
\begin{figure}
\includegraphics[width=.6\textwidth]{img/Marks_found-lines.png}
\caption{Umrisse und Mittellinien der gefundenen Fahrspurmarkierungen}
\label{fig: found markings}
\end{figure}
\subsection{Performance Betrachtung}
sehr schlecht offensichtlich...
\section{Implementierung in eine ROS Node}
\subsection{Performance Betrachtung}
\section{Optimierung durch eigene Implementierung des Canny-Edge-Detectors}
\subsection{Performance Betrachtung}
img/Marks_cannyedges.png

7.31 KiB

img/Marks_classification.png

9.53 KiB

img/Marks_classification_cut1.png

324 B

img/Marks_classification_cut2.png

413 B

img/Marks_original.png

127 KiB

This diff is collapsed.
This diff is collapsed.
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="113.19377mm"
height="48.029472mm"
viewBox="0 0 113.19379 48.029472"
version="1.1"
id="svg5"
inkscape:version="1.2.1 (9c6d41e410, 2022-07-14)"
sodipodi:docname="Topics_makerDetection.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview7"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="mm"
showgrid="false"
inkscape:zoom="1.4142136"
inkscape:cx="277.18585"
inkscape:cy="196.57568"
inkscape:window-width="1920"
inkscape:window-height="1017"
inkscape:window-x="1912"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="g5537"
showguides="true" />
<defs
id="defs2" />
<g
inkscape:label="Ebene 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-28.7088,-85.544655)">
<g
id="g10503"
transform="translate(3.6668863)">
<text
xml:space="preserve"
style="font-size:3.88056px;line-height:1.25;font-family:Arial;-inkscape-font-specification:Arial;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;stroke-width:0.264583"
x="47.803249"
y="92.972412"
id="text10499"><tspan
sodipodi:role="line"
id="tspan10497"
style="text-align:center;text-anchor:middle;stroke-width:0.264583"
x="47.803249"
y="92.972412">/camera_driver</tspan></text>
<path
id="path10501"
style="fill:none;fill-rule:evenodd;stroke:#0d47a1;stroke-width:0.5;stroke-linecap:round;stroke-dasharray:none"
d="m 25.291914,85.794655 h 44.755501 v 6.173514 6.173514 H 62.833183 25.291914 v -6.173514 z"
sodipodi:nodetypes="cccccccc" />
</g>
<g
id="g5537"
transform="translate(6.3222123,-0.412073)">
<path
style="fill:none;stroke:#000000;stroke-width:0.265;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="m 60.177857,98.553756 v 0 7.452714 h 28.391578"
id="path4701"
sodipodi:nodetypes="cccc" />
<path
style="fill:#000000;stroke:none;stroke-width:0.205976px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 88.569435,105.27919 2.00542,0.72728 -2.00542,0.72727 v -0.72727 z"
id="path4703"
sodipodi:nodetypes="ccccc" />
<text
xml:space="preserve"
style="font-size:3.175px;line-height:1.25;font-family:Arial;-inkscape-font-specification:Arial;text-align:start;letter-spacing:0px;word-spacing:0px;text-anchor:start;stroke-width:0.264583"
x="63.784973"
y="104.61102"
id="text5531"><tspan
sodipodi:role="line"
id="tspan5529"
style="font-size:3.175px;text-align:start;text-anchor:start;stroke-width:0.264583"
x="63.784973"
y="104.61102">/img/raw</tspan></text>
</g>
<g
id="g1244"
transform="matrix(-1,0,0,1,257.94475,-5.1261435)">
<path
style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 121.26228,124.47906 v 0 7.45271 h 22.96817"
id="path10168"
sodipodi:nodetypes="cccc" />
<path
style="fill:#000000;stroke:none;stroke-width:0.205976px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 144.23045,131.20449 2.00542,0.72728 -2.00542,0.72727 v -0.72727 z"
id="path10170"
sodipodi:nodetypes="ccccc" />
<text
xml:space="preserve"
style="font-size:3.175px;line-height:1.25;font-family:Arial;-inkscape-font-specification:Arial;text-align:start;letter-spacing:0px;word-spacing:0px;text-anchor:start;stroke-width:0.264583"
x="-138.67474"
y="130.53632"
id="text10174"
transform="scale(-1,1)"><tspan
sodipodi:role="line"
id="tspan10172"
style="font-size:3.175px;text-align:start;text-anchor:start;stroke-width:0.264583"
x="-138.67474"
y="130.53632">/img/color</tspan></text>
</g>
<g
id="g10188"
transform="matrix(-1,0,0,1,196.86033,13.214155)">
<path
style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 60.177857,98.553756 v 0 7.452714 h 60.962753"
id="path10180"
sodipodi:nodetypes="cccc" />
<path
style="fill:#000000;stroke:none;stroke-width:0.205976px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 121.14061,105.27919 2.00542,0.72728 -2.00542,0.72727 v -0.72727 z"
id="path10182"
sodipodi:nodetypes="ccccc" />
<text
xml:space="preserve"
style="font-size:3.175px;line-height:1.25;font-family:Arial;-inkscape-font-specification:Arial;text-align:start;letter-spacing:0px;word-spacing:0px;text-anchor:start;stroke-width:0.264583"
x="-76.813637"
y="104.61102"
id="text10186"
transform="scale(-1,1)"><tspan
sodipodi:role="line"
id="tspan10184"
style="font-size:3.175px;text-align:start;text-anchor:start;stroke-width:0.264583"
x="-76.813637"
y="104.61102">/img/gray</tspan></text>
</g>
<g
id="g1201"
transform="translate(-23.79435,26.840386)">
<path
style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 60.177857,98.553756 v 0 7.452714 h 31.037411"
id="path1193"
sodipodi:nodetypes="cccc" />
<path
style="fill:#000000;stroke:none;stroke-width:0.205976px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 91.215268,105.27919 2.00542,0.72728 -2.00542,0.72727 v -0.72727 z"
id="path1195"
sodipodi:nodetypes="ccccc" />
<text
xml:space="preserve"
style="font-size:3.175px;line-height:1.25;font-family:Arial;-inkscape-font-specification:Arial;text-align:start;letter-spacing:0px;word-spacing:0px;text-anchor:start;stroke-width:0.264583"
x="63.784973"
y="104.61102"
id="text1199"><tspan
sodipodi:role="line"
id="tspan1197"
style="font-size:3.175px;text-align:start;text-anchor:start;stroke-width:0.264583"
x="63.784973"
y="104.61102">/img/lanemarkings</tspan></text>
</g>
<g
id="g972"
transform="translate(3.6668863,27.252459)">
<text
xml:space="preserve"
style="font-size:3.88056px;line-height:1.25;font-family:Arial;-inkscape-font-specification:Arial;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;stroke-width:0.264583"
x="47.803249"
y="92.972412"
id="text968"><tspan
sodipodi:role="line"
id="tspan966"
style="text-align:center;text-anchor:middle;stroke-width:0.264583"
x="47.803249"
y="92.972412">/lane_marker_detection</tspan></text>
<path
id="path970"
style="fill:none;fill-rule:evenodd;stroke:#0d47a1;stroke-width:0.5;stroke-linecap:round;stroke-dasharray:none"
d="M 70.047415,85.794655 H 25.291914 v 6.173514 6.173514 h 7.214232 37.541269 v -6.173514 z"
sodipodi:nodetypes="cccccccc" />
</g>
<g
id="g10511"
transform="translate(71.605153,13.626228)">
<text
xml:space="preserve"
style="font-size:3.88056px;line-height:1.25;font-family:Arial;-inkscape-font-specification:Arial;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;stroke-width:0.264583"
x="47.803249"
y="92.972412"
id="text10507"><tspan
sodipodi:role="line"
id="tspan10505"
style="text-align:center;text-anchor:middle;stroke-width:0.264583"
x="47.803249"
y="92.972412">/image_undistorter</tspan></text>
<path
id="path10509"
style="fill:none;fill-rule:evenodd;stroke:#0d47a1;stroke-width:0.5;stroke-linecap:round;stroke-dasharray:none"
d="m 25.291914,85.794655 h 44.755501 v 6.173514 6.173514 H 62.833183 25.291914 v -6.173514 z"
sodipodi:nodetypes="cccccccc" />
</g>
</g>
</svg>
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment