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
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% 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( image, 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(image); %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
Zu Beginn wurden mithilfe der von Matlab zur Verfügung gestellten App "Color Thresholder" Bildmasken entwickelt, die Ampelfarben anzeigen und den Hintergrund ausblenden sollten. Da die App ein gewünschtes Beispielbild in den einzelnen Farbkanälen der Farbräume RGB, HSV, YCbCr und L*A*B* anzeigt, konnte an dieser Stelle analysiert werden, welcher Farbraum sich am besten für die Erkennung der Ampelphasen eignet (siehe Abb. 1). Der HSV-Farbraum stellte sich dabei als am geeignetster heraus, da sich die Ampelfarben hier am deutlichsten in den einzelnen Kanälen hervorheben. Um den Hintergrund und die unrelevanten Bildbereiche zu entfernen, wurden in der Applikation verschiedene Schwellwerte der einzelnen Farbkanäle getestet. Pixelwerte, die außerhalb der definierten Grenzen liegen werden mit einer 0 überschrieben und schwarz dargestellt. Den Einfluss dieser Schwellwerte werden direkt am ausgewählten Beipsielbild visualisiert, wodurch die Anpassung der einzelnen Parameter sehr einfach aber auch sehr genau umgesetzt werden kann. Ist eine zufriedenstellende Maske gefunden, kann daraus eine Matlab-Funktion generiert werden. Diese Funktionen wurden für die Ampelfarben grün, gelb und rot erstellt. Ein Aufruf der Funktion liefert die entsprechenden Bildbereiche als Binärbild und als Bild-Maske, in der die Farbinformationen erhalten bleiben.
Die 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;
Vorgehen
Erstellung von Farbmasken zur Ampelfarben-Erkennung
Zu Beginn wurden mithilfe der von Matlab zur Verfügung gestellten App "Color Thresholder" Bildmasken entwickelt, die Ampelfarben anzeigen und den Hintergrund ausblenden sollten. Da die App ein gewünschtes Beispielbild in den einzelnen Farbkanälen der Farbräume RGB, HSV, YCbCr und L*A*B* anzeigt, konnte an dieser Stelle analysiert werden, welcher Farbraum sich am besten für die Erkennung der Ampelphasen eignet (siehe Abb. 1). Der HSV-Farbraum stellte sich dabei als am geeignetster heraus, da sich die Ampelfarben hier am deutlichsten in den einzelnen Kanälen hervorheben. Um den Hintergrund und die unrelevanten Bildbereiche zu entfernen, wurden in der Applikation verschiedene Schwellwerte der einzelnen Farbkanäle getestet. Pixelwerte, die außerhalb der definierten Grenzen liegen werden mit einer 0 überschrieben und schwarz dargestellt. Den Einfluss dieser Schwellwerte werden direkt am ausgewählten Beipsielbild visualisiert, wodurch die Anpassung der einzelnen Parameter sehr einfach aber auch sehr genau umgesetzt werden kann. Ist eine zufriedenstellende Maske gefunden, kann daraus eine Matlab-Funktion generiert werden. Diese Funktionen wurden für die Ampelfarben grün, gelb und rot erstellt. Ein Aufruf der Funktion liefert die entsprechenden Bildbereiche als Binärbild und als Bild-Maske, in der die Farbinformationen erhalten bleiben.
Die generierte Matlab-Funktion sieht beispielsweise wie folgt aus:
function [BW,maskedRGBImage] = FindYellow(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 08-Jun-2016
%------------------------------------------------------
% Convert RGB image to chosen color space
I = rgb2hsv(RGB);
% Define thresholds for channel 1 based on histogram settings
channel1Min = 0.047;
channel1Max = 0.091;
% Define thresholds for channel 2 based on histogram settings
channel2Min = 0.892;
channel2Max = 1.000;
% Define thresholds for channel 3 based on histogram settings
channel3Min = 0.895;
channel3Max = 1.000;
% 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;
Bildbereinigung
Für die weitere Verarbeitung werden die einzelnen Binärbilder der Funktionen verwendet. Es werden geschlossene Strukturen ausgefüllt, da der hellste Bereich der Ampel meistens in der Mitte des Kreises liegt und bei eng gewählten Schwellwerten herausgefiltert werden. Da die drei Ampelscheiben immer rund sind, kann davon ausgegangen werden, dass das Ampelobjekt dennoch geschlossen ist und der Fehler damit ausgeglichen werden kann. Außerdem wird eine Mindestanzahl an zusammenhängenden Pixeln festgelegt. Wird diese Anzahl unterschritten, wird dieses Objekt ebenfalls aus dem Binärbild entfernt und kann ignoriert werden. Somit werden Objekte, die nah an den Schwellwerten liegen und in einzelnen Pixeln die Farbmaske passieren herausgefiltert. Als Resultat erhält man ein bereinigtes Binärbild, in dem sich ausschließlich größere Objekte der entsprechenden Farbe befinden. Bereits zu diesem Zeitpunkt kann davon ausgegangen werden, dass es sich bei dem Objekt um eine Ampel handelt.
Ampelphasenerkennung
Aus den drei vorliegenden Binärbildern werden nun die vorliegenden Segmente gelabelt und gezählt. Außerdem wird das größte Farbelement der jeweiligen Bilder ermittelt. Die Ampelphasenerkennung wird dann über das größte Farbobjekt ermittelt. Handelt es sich bei dem größten Objekt um ein Rotes oder Gelbes Element, wird auch überprüft, ob die entsprechend andere Farbe ungefähr die gleiche Größe besitzt. Ist dies der Fall handel es sich um eine Gelb-Rot-Phase. Sind jeweils keine Bildobjekte vorhanden, wurde keine Ampelphase erkannt.
Zusammenfassung
Ausblick
Literaturverzeichnis
Korrektur/Rückmeldungen
Hier können Nutzer oder kritische Leser (meist Professoren) Verbesserungen fordern/vorschlagen.