SigSys15 Spurerkennung
Autor: Sergej Krause
Betreuer: Prof. Schneider
Motivation
Eine Aufgabe beim Carolo Cup ist der "Rundkurs mit und ohne Hindernissen". Hierbei ist das Fahrzeug autonom in der Spur zu halten.
Ziel
Die Fahrbahnbegrenzungen links, rechts und mitte soll robust erkannt und unterschieden werden. Aus den Begrenzungslinien ist eine Kurvenkrümmung oder der parabolische Kurvenverlauf in Weltkoordinaten zu ermitteln.
Aufgabe
- Der aufgezeichnete Rundkurs liegt als Video vor.
- Lesen Sie diesen als Endlosschleife in Matlab ein.
- Identifizieren Sie während der virtuellen Fahrt die Fahrbahnbegrenzungen mit Matlab.
- Vermeiden Sie Fehler ("false-positives").
- Filtern Sie die 3 Linien möglichst stabil zu einer Sollfahrspur.
- Optimieren Sie die Rechenzeit Ihres Algorithmus.
Lösungen
Erste Schritte
Bearbeitung des Videos
Um die Bearbeitung der einzelnen Frames in einer annehmbaren Zeit durchzuführen werden alle Frames des Videso erst als Imagedateien abgespeichert und wenn die Frames bearbeitet werden sollen können Sie wieder geladen werden. Mit "workingDir" kann entschieden werden in welches Verzeichnis die Bilder gespeichert werden.
readVideo = VideoReader('Rundkurs.mp4');
% choose working directory
workingDir = 'Img_Vid_temp';
% split video into Frames and create imagefolder
VideoToImg(workingDir, readVideo, 'max');
% get all filenames from the directory
imageNames = dir(fullfile(workingDir,'images','*.png'));
imageNames = {imageNames.name}';
% amount of images read from directory
maxImages = length(imageNames);
In der Funktion VideoToImg wird überprüft ob das angegebene Verzeichnis existitiert, wenn nicht wird es neu erstellt. Die Frames werden nummeriert und als *.png abgespeichert um einen einfachen Zugriff sicherzustellen.
%*******************************************%
% Einlesen des Videos und in einzelne %
% Frames zerlegen, die dann in einem %
% Unterordner, der generiert wird, falls %
% vorhanden erst gelöscht wird um mögliche%
% Fehlerquellen zu umgehen,dann in %
% diesem gespeichert werden %
% Parameter: %
% In: %
% -workingDir: arbeitsverzeichnis %
% in dem Ordner erst gelöscht,%
% falls vorhanden, dann neu %
% erzeugt werden %
% -readVideo: muss eine %
% eingelesene Videodatei %
% enthalten, die in Frames %
% gesplittet wird %
% -maxFrames: gibt die Anzahl der %
% zu erzeugenden Frames an, %
% falls 'max' dann wird die %
% maximale Frameanzahl %
% ermittelt und benutzt %
% Out: %
% -nix %
%*******************************************%
function [] = VideoToImg (workingDir, readVideo, maxFrames)
% Create Workspace and delete previous
% folder to save the images into it
if exist( workingDir, 'dir')
rmdir(workingDir, 's');
end
mkdir(workingDir);
mkdir(workingDir,'images');
% Get the maximum number of frames in
% the video
if strcmp(maxFrames,'max')
maxFrames = get(readVideo, 'NumberOfFrames');
else
maxFrames = str2double(maxFrames);
end
% readVideo has to be recreated after
% get(readVideo, 'NumberOfFrames')
readVideo = VideoReader('Rundkurs.mp4');
% Settings for easier control of progress
loopSize = maxFrames; % to cover whole video
reverseStr = ''; % necessary to show the progress
% Breaking video into frames
for index = 1:1:loopSize
video_Frames = readFrame(readVideo);
filename = [sprintf('Frame_%04d',index),'.png'];
fullname = fullfile(workingDir,'images',filename);
imwrite(video_Frames,fullname);
%showing progress
percentDone = 100* index / loopSize;
msg = sprintf('Image creation: %3.1f', percentDone);
fprintf([reverseStr,msg]);
reverseStr = repmat(sprintf('\b'),1,length(msg));
end
end
Bearbeitung der einzelnen Frames
Um eine vernünftige Erkennung der Spur zu gewährleisten wird das Bild entzerrt. Dies geschieht mit den Kameraparametern VRM_Calib. Damit wird die Verzerrung der horizontalen Linien des Bildes durch die Kamera aufgehoben. Desweiteren muss das Farbbild in ein Schwarz-Weiß-Bild konvertiert werden um einen Sobel-filter zur Kanten detektion anwenden zu können. Da das Fahrzeug im Bild ist und hin und wieder die Spurlinie kreuzt wird das Fahrzeug mit einer schwarzen Maske überdeckt.
orgFrame = imread(fullfile(workingDir,'images',imageNames{index}));
% make image blacke and white, put a black mask of the car to
% improve performance and show only the edges, sobel for fastes
% performance
bwFrame = undistortImage(orgFrame,cameraParams_15_05_14);
bwFrame = im2bw(bwFrame,0.6);
bwFrame = bwFrame .*maske_auto;
bwFrame = edge(bwFrame,'sobel');
Endlosschleife
Die Endlosschleife wird erzeugt in dem eine while 1 Schleife um die eigentliche Bildbearbeitungsschleife gelegt wird. Zu beachten ist das in der Bildbearbeitungsschleife ein Video erzeugt wird und dieses in einer Endlosschleife sehr groß werden kann. Um dies zu verhindern kann ein IF abfrage gemacht werden um das Video nur beim ersten Durchlauf zu erzeugen.
firstRun = 0;
while 1
for index = 1:1:maxImages
orgFrame = imread(fullfile(workingDir,'images',imageNames{index}));
% make image blacke and white, put a black mask of the car to
% improve performance and show only the edges, sobel for fastes
% performance
bwFrame = undistortImage(orgFrame,cameraParams_15_05_14);
bwFrame = im2bw(bwFrame,0.6);
bwFrame = bwFrame .*maske_auto;
bwFrame = edge(bwFrame,'sobel');
.
.
.
if firstRun == 0
writeVideo(outputVideo,orgFrame);
firstRun = 1;
end
end
end
Die Spurerkennung
Die Spurerkennung erfolgt nach einem simplen Muster:
- Suche ab der Mitte des Bildes nach rechts ein weißes Pixel
- wenn ein weißes Pixel gefunden wurde suche nach einem weiteren weißen Pixel auf der X-Achse -> 4
- wenn kein weißes Pixel gefunden wurde inkrementiere den Y-Wert um 1 -> 1
- wenn das zweite weiße Pixel gefunden wurde, überprüfe ob der Abstand kleiner als 20 ist -> 6
- wenn kein zweites weißes Pixel gefunden wurde inkrementiere den Y-Wert um 1 -> 1
- wenn der Abstand kleiner als 20 ist, markiere den Bereich zwischen den Pixeln, inkrementiere den Y-Wert um 1 -> 1
- wenn der Abstand gößer ist als 20 inkrementiere den Y-Wert um 1 -> 1
while endSearch == 0 && emptyCounter < 20
foundPoI_start = findWhiteinLine(bwFrame, PoI,'right');
if abs(historyPoI(2)-foundPoI_start(2)) > 5
emptyCounter = emptyCounter + 1;
else if foundPoI_start == 0
emptyCounter = emptyCounter + 1;
else
%+5 because next pixel can also be white and to have a bit tolerance
foundPoI_end = findWhiteinLine(bwFrame, [foundPoI_start(1) (foundPoI_start(2)+5)],'right');
lineWidth = foundPoI_end(2) - foundPoI_start(2);
if lineWidth >= 20
emptyCounter = emptyCounter + 1;
else if (foundPoI_end == 0)
emptyCounter = emptyCounter + 1;
else
emptyCounter = 0;
historyPoI = foundPoI_start;
% paint orignial frame from first found whitepixel
% to the second found whitepixel
markingLength = markingLength - 1;
orgFrame(foundPoI_start(1),foundPoI_start(2):foundPoI_end(2),1) = 255;
orgFrame(foundPoI_start(1),foundPoI_start(2):foundPoI_end(2),2) = 0;
orgFrame(foundPoI_start(1),foundPoI_start(2):foundPoI_end(2),3) = 0;
end
end
end
end
PoI(1) = PoI(1) - 1;
if markingLength == 0
endSearch = 1;
end
end
Fazit und Ausblick
Weblinks
→ zurück zum Hauptartikel: Signalverarbeitende Systeme SoSe2015