Ampelphasenerkennung
Autor: Niklas Lingenauber
Betreuer: Prof. Schneider
Einleitung
Dieser Artikel wurde für die Übung des Kurses Digitale Signal- und Bildverarbeitung verfasst und dokumentiert die Ergebnisse des Projektes "Ampelphasenerkennung". Die allgemeinen Ziele der Projekte können im Wiki-Artikel DSB SoSe2016 eingesehen werden.
Aufgabenstellung
Die Aufgabe lautete eine Algorithmus zu implementieren, der aus einer aufgezeichenten Autofahrt erkennt, ob sich eine Ampel auf den einzelnen Frames befindet und welche Ampelphase diese zeigt. Das Video sollte mit einer an der Front des Autos angebrachten Kamera erstellt werden. Als Entwicklungsumgebung sollte Matlab verwendet werden.
Algorithmus
Main-Funktion
Die Main-Funktion initialisiert die Ampelphasenerkennung und ruft die einzelnen Funktionen zur Erkennung der Ampelphasen auf. Bei Start dieser Funktion wird nach der einrichtung benötigter Variablen ein User-Interface geöffnet, über das der Anwender ein Video im *.mp4-Format oder ein Bild im *.jpg-Format auswählen kann. Anschließend wird abhängig von den Maßen des betrachteten Bildes bzw. Frames eines Videos eine Region Of Interest (ROI) definiert. Da von einer starren Kamera in der Fahrzeug-Front ausgegangen wurde, kann ein festgesetzter Anteil im unteren Teil des Bildes vernachlässigt werden, da sich ampeln immer im mittleren bis oberen Segment befinden und so eine Fehlinterpretation von Bremsleuchten verhindert werden kann. Verschiedene Tests haben gezeigt, dass man die besten Ergebnisse beim Betrachten der oberen 5/8 des Bildes erhält. Nach dem Zuschneiden des Bildes werden die einzelnen Ampelphasen dedektiert. Dazu wurden Funktionen geschrieben die im nächsten KApitel genauerer erläutert werden. Als Rückgabewert liefern diese die Koordinaten und die Radien von Kreisen, in denen sich vermutlich das Leuchten einer Ampel befindet. Diese Informationen werden für die jeweilige Farbe getrennt gespeichert. Nach der Ausgabe des originalen unbeschnittenen Bildes werden diese Kreise in der jeweiligen Ampelfarbe in das Bild eingezeichnet. Wird eine bestimmte Anzahl an Kreisen je Farbe ermittelt, handelt es sich vermutlich um eine Fehlinterpretation und die Ampeln werden nicht eingezeichnnet. Bei der Bearbeitung eines Videos werden diese Schritte wiederholt für jedes betrachtete Frame durchgeführt. Um die Geschwindigkeit des Algorithmusses zu erhöhen, werden die Frames nur in bestimmten Abständen betrachtet.
Die Main-Funktion lautet wie folgt:
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Digitale Signal und Bildverarbeitung %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Projekt: Ampelphasenerkennung %
% %
% Autor: Niklas Lingenauber (2132521) %
% %
% Studiengang: Mechatronik 6. Semester %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% %
% Beschreibung: Diese main-Datei initialisiert und %
% startet die Ampelphasenerkennung. Sie %
% liest eine Datei ein, ruft die nötigen %
% Funktionen auf und sorgt für die %
% Bildausgabe. %
% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% %
% Implementierung: MatLab 2015b %
% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% %
% letzte Änderung: 20.06.2016 %
% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
clc;
clear all;
%% Initialisierung %%
%Parameter zur Kreissuche
RadiusMin=20; %Kleinster zu erkennender Kreis
RadiusMax=60; %größter zu erkennender Kreis
SensityRed=0.65; %Sensibilität der Kreissuche (Rot)
SensityYellow=0.70; %Sensibilität der Kreissuche (Gelb)
SensityGreen=0.80; %Sensibilität der Kreissuche (Grün)
%Strukturelement zur Dilatatation
SE = strel('disk', RadiusMin, 0); %Kreis mit dem Radius des kleinsten zu erkennenden Kreises
%% Datei einlesen %%
[FileName, path] = uigetfile({'*.mp4';'*.jpg'},... %Datei auswählen
'Bitte wählen Sie ein Bild oder ein Video aus');
[~, ~, Format]=fileparts(FileName); %Format ermitteln
if strcmpi(Format,'.jpg')
image = imread([path, FileName]); %Bild einlesen
elseif strcmpi(Format,'.mp4')
video = VideoReader([path, FileName]); %Video einlesen
else
error('Unbekanntes Dateiformat. Programm wurde abgebrochen.'); %falsches Format
end
if exist('image','var')==1
[height, width]=size(image(:,:,1)); %Höhe des Videos einlesen
UntergrenzeROI=round(height*5/8); %Fahrzeuge rausschneiden, da Bremslicht stört
imageROI=image(1:UntergrenzeROI,1:width,:); %Bild zuschneiden, nur oben betrachten
%Ampelphasen finden
[centers_Red, radii_Red]=FilterRed(imageROI, SE, RadiusMin, RadiusMax, SensityRed); %Rot
[centers_Yellow, radii_Yellow]=FilterYellow(imageROI, SE, RadiusMin, RadiusMax, SensityYellow); %Gelb
[centers_Green, radii_Green]=FilterGreen(imageROI, SE, RadiusMin, RadiusMax, SensityGreen); %Grün
%Ausgabe
figure(1)
imshow(image)
if length(centers_Red)<=5
viscircles(centers_Red, radii_Red,'EdgeColor','r'); %Rotes Ampellicht einzeichnen
end
if length(centers_Yellow)<=4
viscircles(centers_Yellow, radii_Yellow,'EdgeColor','y'); %Gelbes Ampellicht einzeichnen
end
if length(centers_Green)<=5
viscircles(centers_Green, radii_Green,'EdgeColor','g'); %Grünes Ampellicht einzeichnen
end
drawnow;
else
Position=0; %Position im Video
StepSize=2; %Schritweite im Video zur beschleunigung des Algorithmus
while hasFrame(video)
image=readFrame(video); %Frame einlesen
image = impyramid(image, 'reduce'); %Bild verkleinern
[height, width]=size(image(:,:,1)); %Höhe des Videos einlesen
UntergrenzeROI=round(height*5/8); %Fahrzeuge rausschneiden, da Bremslicht stört
imageROI=image(1:UntergrenzeROI,1:width,:); %Bild zuschneiden, nur oben betrachten
%Ampelphasen finden
[centers_Red, radii_Red]=FilterRed(imageROI, SE, RadiusMin, RadiusMax, SensityRed); %Rot
[centers_Yellow, radii_Yellow]=FilterYellow(imageROI, SE, RadiusMin, RadiusMax, SensityYellow); %Gelb
[centers_Green, radii_Green]=FilterGreen(imageROI, SE, RadiusMin, RadiusMax, SensityGreen); %Grün
%Ausgabe
figure(1)
imshow(image)
if length(centers_Red)<=5
viscircles(centers_Red, radii_Red,'EdgeColor','r'); %Rotes Ampellicht einzeichnen
end
if length(centers_Yellow)<=4
viscircles(centers_Yellow, radii_Yellow,'EdgeColor','y'); %Gelbes Ampellicht einzeichnen
end
if length(centers_Green)<=5
viscircles(centers_Green, radii_Green,'EdgeColor','g'); %Grünes Ampellicht einzeichnen
end
drawnow; %Bild sofort ausgeben
Position=Position+StepSize; %Position im Video anpassen
video.CurrentTime=Position;
end
end
Funktionen zur Ampelphasenerkennung
function [ centers, radii] = FilterYellow( img, SE, RadiusMin, RadiusMax, Sensity )
%Diese Funktion filtert aus dem Bild image die Pixel heraus, die der roten
%Ampelfarbe entsprechen. Anschließend werden Kreise im gefilterten Bild
%gesucht und zurückgegeben.
%
% Letzte Änderung: 18.06.2016
% Autor: Niklas Lingenauber
[BW, ~]=Yellow(img); %rote Ampelfarbe finden
Dilate=imdilate(BW,SE); %Dilatation durchführen
[centers, radii] = imfindcircles(Dilate,[RadiusMin RadiusMax],...
'ObjectPolarity','bright', 'Sensitivity', Sensity); %Helle Kreise auf dunklem Untergrund suchen
end
Farbfilter
Zur Filterung der jeweiligen Ampelfarbe wurde mit der von Matlab zur Verfügung gestellten App "Color Thresholder" Bildmasken entwickelt, in denen Pixel mit dem gewünschten Farbwert durch eine 1 markiert und somit weiß dargsetllt werden. Pixel, die diesem Kriterium nicht entsprechen werden als 0 gespeichert und schwarz abgebildet. Aus den definierten Filtern lassen sich anschließend automatisiert Matlab-Funktionen generieren. Der Hintergrund und andere störende Objekte werden somit aus dem Bild entfernt und das daraus entstandene Binärbild kann zur weiteren Bildverarbeitung verwendet werden. Ein großer Vorteil App ist, dass ein gewünschtes Beispielbild in den einzelnen Farbkanälen der Farbräume RGB, HSV, YCbCr und L*A*B* anzeigt werden kann und eine Analyse, welcher Farbraum sich am besten für die Erkennung der Ampelphasen eignet erleichtert wird (siehe Abb. 1). Der HSV-Farbraum stellte sich dabei als am besten geeignet heraus, da sich die Ampelfarben hier am deutlichsten in den einzelnen Kanälen hervorheben. Um das bestmögliche Filterergebnis für eine jeweilige Farbe zu erhalten, wurden zur Ermittlung geeigneter Schwellwerte Bild-Collagen erstellt, in denen sich kritische Objekte wie beispielsweise gelbe Verkehrsschilder oder Baustellenbeleuchtungen befinden erstellt. Anhand dieser Collagen wurden verschiedene Schwellwerte getestet und optimiert. Aufgrund der genannten kritischen Objekte wurden die Filter eng definiert, sodass nur vereinzelte Pixel den Filter passieren. Dadurch wird das detektieren von falschen Pixel stark beschränkt, die Fehler-Anfälligkeit im darauffolgenden Programm steigt jedoch stark.
Eine durch die App "ColorThresholder" generierte Matlab-Funktion sieht beispielsweise wie folgt aus:
function [BW,maskedRGBImage] = Yellow(RGB)
%createMask Threshold RGB image using auto-generated code from colorThresholder app.
% [BW,MASKEDRGBIMAGE] = createMask(RGB) thresholds image RGB using
% auto-generated code from the colorThresholder App. The colorspace and
% minimum/maximum values for each channel of the colorspace were set in the
% App and result in a binary mask BW and a composite image maskedRGBImage,
% which shows the original RGB image values under the mask BW.
% Auto-generated by colorThresholder app on 18-Jun-2016
%------------------------------------------------------
% Convert RGB image to chosen color space
I = rgb2hsv(RGB);
% Define thresholds for channel 1 based on histogram settings
channel1Min = 0.022;
channel1Max = 0.169;
% Define thresholds for channel 2 based on histogram settings
channel2Min = 0.717;
channel2Max = 0.909;
% Define thresholds for channel 3 based on histogram settings
channel3Min = 0.651;
channel3Max = 0.791;
% Create mask based on chosen histogram thresholds
BW = (I(:,:,1) >= channel1Min ) & (I(:,:,1) <= channel1Max) & ...
(I(:,:,2) >= channel2Min ) & (I(:,:,2) <= channel2Max) & ...
(I(:,:,3) >= channel3Min ) & (I(:,:,3) <= channel3Max);
% Initialize output masked image based on input image.
maskedRGBImage = RGB;
% Set background pixels where BW is false to zero.
maskedRGBImage(repmat(~BW,[1 1 3])) = 0;
Zusammenfassung
Ausblick
Literaturverzeichnis
Korrektur/Rückmeldungen
Hier können Nutzer oder kritische Leser (meist Professoren) Verbesserungen fordern/vorschlagen.