Version [82880]
Dies ist eine alte Version von MAEIVorverarbeitungInMatlab erstellt von mstruening am 2017-08-14 14:36:41.
Datenvorverarbeitung in MATLAB
1. Vorbereitung
Für dieses Tutorium wurde Ihnen ein Archiv mit folgenden Inhalten bereitgestellt:
- 100 Bilddateien von Kraftfahrzeugen mit mehr oder weniger erkennbaren Kennzeichen
- Zugehörige INI-Dateien
- Das Werkzeug "KFZ-Expert"
2. Manuelle Vorverarbeitung
Als erstes müssen alle 100 Bilddateien von Ihnen mit dem Programm "KFZ-Expert" bearbeitet werden. "KFZ-Expert" ermöglicht Ihnen das Setzen der ROI und des erwarteten Klassifikationsergebnisses. Wenn Sie das Programm gestartet haben, setzen Sie bitte als Erstes das Verzeichnis mit den Kennzeichen-Bildern aus den Archiv im mittleren Teil des Programms und wählen Sie das erste Bild aus. Sie sollten jetzt in der oberen linken Ecke ein Bild eines Fahrzeugs sehen. Im unteren Teil sehen sie die ausgewählte ROI, die nicht unbedingt optimal gesetzt ist.
Ihre Aufgabe besteht jetzt darin, für alle 100 Bilder die Eckpunkte so zu setzen, dass sich möglichst das vollständige Kennzeichen im roten Rahmen befindet und der Rahmen keine Zeichen schneidet. Außerdem sollten die Kanten des Rahmens zur Ausrichtung der Buchstaben parallel sein, wie es im folgenden Bild zu sehen ist. Zudem sollen Sie den Text des Kennzeichens und ob es vollständig zu sehen ist in der Software in die entsprechenden Felder eintragen. Bitte verzichten Sie auf die Eingabe von Leerzeichen im Kennzeichen und geben sie 0 und 1 anstelle von O und I an. Die Software speichert alle Ihre Änderungen automatisch in einer INI-Datei mit identischem Namen zum Bild.
Steuerung von KFZ-Expert:
- Ein Mausklick ins Originalbild setzt den aktuellen Eckpunkt des Polygons auf die Klickposition.
- Der aktuelle Eckpunkt kann mit der rechten Maustaste durchgeschaltet werden
- Pfeil Hoch und Pfeil Runter auf der Tastatur wechseln zum nächsten bzw. vorherigen Bild
- Ein Mausklick in den unteren Teil des Programms (den skalierten Ausschnitt des Originalbildes) setzt immer den der Klickposition nächsten Eckpunkt, sofern nicht zu weit weg geklickt wird. Somit können die Eckpunkte präzisiert werden.
3. Automatisierte Vorverarbeitung
Die Vorgehensweise der automatisierten Vorverarbeitung soll vorerst nur an einem einzigen Bild demonstriert werden. Für das maschinelle Lernen werden die folgenden Schritte später an allen 100 Bildern wiederholt.
a. Bilder und Metadaten aus zugehörigen INI-Dateien einlesen
Im ersten automatisiertem Schritt laden wir das Bild in den Speicher und lesen die zugehörige INI-Datei aus. Darin finden wir, die von uns zuvor festgelegten Eckpunkte und den Kennzeichentext. Da MATLAB keinen direkten Support für INI-Dateien anbietet, verwenden wir dafür den INI-Reader von Primoz Cermelj, der auf Matlab Central (https://de.mathworks.com/matlabcentral/fileexchange/2976-inifile) zu finden ist. Jeden unserer Schritte stellen wir zur Nachvollziehbarkeit in einem eigenen Subplot dar. Das hat auch den Vorteil, dass Programmierfehler in den einzelnen Schritten besser erkannt werden können und uns somit das Debuggen erleichtert wird. Wir zeichnen schlussendlich noch die Eckpunkte aus der INI-Datei im Bild ein und weisen dem Subplot einen Titel zu.
% Bilder und zugehörige INI-Dateien einlesen
%Dateipfad Eingabedaten
PATH_inputImg = ['data\img00' num2str(dataIndex)];
%Dateipfad Ausgabebild
PATH_outputImg = ['graf\img00' num2str(dataIndex)];
%Eingabebild einlesen
IMG_1_input = imread([PATH_inputImg '.jpg']);
%Eingabebild in Subplot oben-links eintragen
close all, subplot(3,3,1), imshow(IMG_1_input), hold on
%Cell Array für INI-Read konstruieren
% Mit Funktion inifile(iniFileName, mode, modeParams)
% Jede Zeile in CellArray 'readParams' muss enthalten:
% Section, Subsection, Key, Format(s:string, d:double)
%Reihenfolge: Kennzeichentext, x-Koordinaten, y-Koordinaten
section = 'Plate';
readParams = {section,'','name','s';
section,'','x1','d';
section,'','x2','d';
section,'','x3','d';
section,'','x4','d';
section,'','y1','d';
section,'','y2','d';
section,'','y3','d';
section,'','y4','d'};
%INI-Read ausführen
%Ergebnisse liegen in selber Reihenfolge wie mit readParams angefordert
iniValues = inifile([PATH_inputImg '.ini'], 'read', readParams);
%Erste Zeile: Kennzeichentext
plateText = iniValues{1,1};
%Zeile 2-5: x-Koordinaten
x = cell2mat(iniValues(2:5));
%Zeile 6-9: y-Koordinaten
y = cell2mat(iniValues(6:9));
%Polygon-Koordinaten in Original-Bild eintragen
plot(x, y,'g*')
%Bildindex und Kennzeichen als Titel
title(['Bild: ' num2str(dataIndex) ' ' plateText])
%Dateipfad Eingabedaten
PATH_inputImg = ['data\img00' num2str(dataIndex)];
%Dateipfad Ausgabebild
PATH_outputImg = ['graf\img00' num2str(dataIndex)];
%Eingabebild einlesen
IMG_1_input = imread([PATH_inputImg '.jpg']);
%Eingabebild in Subplot oben-links eintragen
close all, subplot(3,3,1), imshow(IMG_1_input), hold on
%Cell Array für INI-Read konstruieren
% Mit Funktion inifile(iniFileName, mode, modeParams)
% Jede Zeile in CellArray 'readParams' muss enthalten:
% Section, Subsection, Key, Format(s:string, d:double)
%Reihenfolge: Kennzeichentext, x-Koordinaten, y-Koordinaten
section = 'Plate';
readParams = {section,'','name','s';
section,'','x1','d';
section,'','x2','d';
section,'','x3','d';
section,'','x4','d';
section,'','y1','d';
section,'','y2','d';
section,'','y3','d';
section,'','y4','d'};
%INI-Read ausführen
%Ergebnisse liegen in selber Reihenfolge wie mit readParams angefordert
iniValues = inifile([PATH_inputImg '.ini'], 'read', readParams);
%Erste Zeile: Kennzeichentext
plateText = iniValues{1,1};
%Zeile 2-5: x-Koordinaten
x = cell2mat(iniValues(2:5));
%Zeile 6-9: y-Koordinaten
y = cell2mat(iniValues(6:9));
%Polygon-Koordinaten in Original-Bild eintragen
plot(x, y,'g*')
%Bildindex und Kennzeichen als Titel
title(['Bild: ' num2str(dataIndex) ' ' plateText])
b. Maskierung
Als Nächstes verwerfen wir alle Informationen im Bild, die für die Kennzeichenerkennung unerheblich ist. Wir erstellen ein Polygon-Maske aus den Koordinaten-Vektoren x und y und stellen diese als Subplot dar. Danach multiplizieren wir die Pixelfarben der Maske und des Originalbilds elementweise miteinander. Das Ergebnis ist ein größtenteils geschwärztes Bild mit isoliertem Kennzeichen. Dieses tragen wir ebenfalls als Subplot ein.
% Maskierung
%Maske aus Polygon-Koordinaten erzeugen
IMG_2_mask = roipoly(IMG_1_input, x, y);
%Maske als Subplot oben-mitte eintragen
subplot(2,3,2), imshow(IMG_2_mask), title('Maske')
%Bilddaten außerhalb der Maske schwärzen
IMG_3_cutOut = uint8(IMG_2_mask) .* IMG_1_input;
%Ergebnis als Subplot eintragen
subplot(3,3,3), imshow(IMG_3_cutOut), title('Ausschnitt')
%Maske aus Polygon-Koordinaten erzeugen
IMG_2_mask = roipoly(IMG_1_input, x, y);
%Maske als Subplot oben-mitte eintragen
subplot(2,3,2), imshow(IMG_2_mask), title('Maske')
%Bilddaten außerhalb der Maske schwärzen
IMG_3_cutOut = uint8(IMG_2_mask) .* IMG_1_input;
%Ergebnis als Subplot eintragen
subplot(3,3,3), imshow(IMG_3_cutOut), title('Ausschnitt')
c. Kennzeichen begradigen
Der nächste Schritt ist die Begradigung der oberen und unteren Kante des Kennzeichens. Dazu errechnen wir die Steigungen der beiden Kantengeraden und berechnen ihr Mittel. Mithilfe des Arkustangens ermitteln wir den Winkel dieses Anstiegs und konvertieren diesen anschließend von Radiant in Bogenmaß. Die Umwandlung ist für den Funktionsaufruf imrotate() notwendig. Damit rotieren wir das zuvor ausgeschnittene Bild so, dass die obere und untere Kante begradigt werden.
% Begradigung oberer und unterer Kante
%Anstieg aus Punkten P1-P4 berechnen (oben-links & oben-rechts)
m1 = (y(1) - y(4)) / (x(1) - x(4));
%Anstieg aus Punkten P2-P3 berechnen (unten-links & unten-rechts)
m2 = (y(2) - y(3)) / (x(2) - x(3));
%Winkel des Anstieges aus dem Mittel beider Geradenanstiege berechnen
alphaInRad = atan((m1 + m2) / 2);
%Winkel von Rad in Deg konvertieren
alphaInDeg = alphaInRad * 180 / pi;
%Bild um den Winkel rotieren
IMG_4_rotated = imrotate(IMG_3_cutOut, alphaInDeg, 'bilinear', 'crop');
%Subplot eintragen
subplot(3,3,4), imshow(IMG_4_rotated), title('Begradigung')
hold on
%Polygon-Koordinaten rot einzeichnen
plot(x(:), y(:), 'r.');
%Anstieg aus Punkten P1-P4 berechnen (oben-links & oben-rechts)
m1 = (y(1) - y(4)) / (x(1) - x(4));
%Anstieg aus Punkten P2-P3 berechnen (unten-links & unten-rechts)
m2 = (y(2) - y(3)) / (x(2) - x(3));
%Winkel des Anstieges aus dem Mittel beider Geradenanstiege berechnen
alphaInRad = atan((m1 + m2) / 2);
%Winkel von Rad in Deg konvertieren
alphaInDeg = alphaInRad * 180 / pi;
%Bild um den Winkel rotieren
IMG_4_rotated = imrotate(IMG_3_cutOut, alphaInDeg, 'bilinear', 'crop');
%Subplot eintragen
subplot(3,3,4), imshow(IMG_4_rotated), title('Begradigung')
hold on
%Polygon-Koordinaten rot einzeichnen
plot(x(:), y(:), 'r.');
d. Polygon-Koordinaten an Begradigung anpassen
Durch die Rotation des Bildes in Schritt c) stimmen die Eckpunkte des Kennzeichens nicht mehr mit den angegebenen Polygon-Eckpunkten aus unserer INI-Datei überein. Die Rotation, die wir am Bild vorgenommen, müssen wir deshalb identisch auf die Polygon-Eckpunkte übertragen. Da dafür kein spezieller Befehl zur Verfügung steht, muss dies händisch durchgeführt werden. Wir erzeugen also erst einmal eine Rotationsmatrix R mit dem Winkel alphaInRad um den wir zuvor begradigt haben und definieren unseren Rotationsursprung in der Mitte des Bildes. Eine einfache Multiplikation mit der Rotationsmatrix würde um den Nullpunkt rotieren. Da wir aber um den definierten Rotationsursprung origin rotieren wollen, müssen wir diesen zuvor von der Punktkoordinate subtrahieren. Das Ergebnis muss transponiert werden, damit Matrix und Vektor multipliziert werden können. Durch die Multiplikation mit der Rotationsmatrix erfolgt dann die eigentliche Rotation. Um den Rotationsprozess abzuschließen, addieren wir den Rotationsursprung wieder auf. Zur Übersicht zeichnen wir die alten Koordinaten rot und die neuen Koordinaten grün in einen Subplot ein.
% Polygon-Koordinaten transformieren
%Rotationsmatrix erzeugen
R = [cos(alphaInRad) sin(alphaInRad); -sin(alphaInRad) cos(alphaInRad)];
%Koordinatenursprung in Bildmitte
origin = [320 240];
%Einzelne Eckpunkte transformieren
for k = 1:4
vertex = [x(k) y(k)];
vertex = R * (vertex - origin)' + origin';
x_corrected(k) = vertex(1);
y_corrected(k) = vertex(2);
end
% Neue Koordinaten (grün) und alte Koordinaten (rot) plotten
subplot(3,3,5), imshow(IMG_4_rotated), title('Punkttransformation'), hold on
plot(x, y, 'r+')
plot(x_corrected, y_corrected, 'g.')
%Rotationsmatrix erzeugen
R = [cos(alphaInRad) sin(alphaInRad); -sin(alphaInRad) cos(alphaInRad)];
%Koordinatenursprung in Bildmitte
origin = [320 240];
%Einzelne Eckpunkte transformieren
for k = 1:4
vertex = [x(k) y(k)];
vertex = R * (vertex - origin)' + origin';
x_corrected(k) = vertex(1);
y_corrected(k) = vertex(2);
end
% Neue Koordinaten (grün) und alte Koordinaten (rot) plotten
subplot(3,3,5), imshow(IMG_4_rotated), title('Punkttransformation'), hold on
plot(x, y, 'r+')
plot(x_corrected, y_corrected, 'g.')