Legoteil-Erkennung/Klassifizierung B

Aus HSHL Mechatronik
Zur Navigation springen Zur Suche springen

Autor: Tim Dienwiebel
Betreuer: Prof. Schneider

Motivation

Abbildung 1: Originalbild des Bauteils auf dem Förderband

Im Sommersemester 2015 wurde im SDE Praktikum erstmals ein anderes Projekt als das autonome Fahrzeug behandelt. Es wurde damit begonnen, eine Legoteil Zählmaschine zu entwickeln. Von Null auf wurden Teillösungen und Konzepte erstellt, die im Laufe des nächsten Semesters zu einer funktionierenden Maschine zusammengefügt werden sollen.

Die Hauptaufgabe der Legoteil Zählmaschine ist das Erkennen und Zählen von Legoteilen, welche in einem chaotischen System auf ein Förderband gelegt werden.
Das Projekt im Fach Digitale Signal- und Bildverarbeitung bestand daher daraus, vier frei wählbare Legoteile zu fotografieren, die Bilder in Matlab einzulesen und das Legoteil zu identifizieren. Zur Identifizierung sollte ein Algorithmus in Matlab implementiert werden, welcher auf den Stoff aufbaut, der im Fach der Digitalen Signal- und Bildverarbeitung im selben Semester gelehrt wurde.

Ziel

Vier Legoteile sollen fehlerfrei erkannt werden und zusammen mit ihrer Teilenummer und der Anzahl an gezählten Teilen in Matlab hinterlegt werden.

Aufgabe

  1. Fotografieren Sie 4 Legoteile ihrer Wahl mit einer Webcam auf einem Musterstück eines geeigneten Förderbandes.
  2. Lesen Sie die Bilder in Matlab ein.
  3. Identifizieren Sie die sich auf dem Förderband befindlichen Legoteile und ordnen diese somit ihren Teilenummern zu.
  4. Vermeiden Sie Fehler in der Erkennung durch eine geeignete Klassifizierung und durch Segmentierung.

Lösung von Tim Dienwiebel

Schematische Beschreibung des Algorithmus

Abbildung 2: Schematische Funktionserklärung

Abbildung 2 beschreibt Schematisch die Funktion des Matlab Skriptes.

Allgemeine Informationen zur Umsetzung

Vor Bearbeitung der Aufgabe wurde ein Prototyp der zu erwartenden Bildverarbeitungsbox der Legoteilzählmaschine im SDE Praktikum konstruiert. Es wurde eine Lichtquelle am Deckel der Bildverarbeitungsbox montiert, welche rund um die Logitech Webcam C920 eine homogene Beleuchtung des Bodens gewährleistet.
Die ausgewählten Legoteile wurden dann auf einem Muster der Bandart „SuperGrip“ mittels der „Image Aquisition Toolbox“ in Matlab abgelichtet und direkt als ‘.mat‘-Datei abgespeichert [Siehe Abbildung 1]. Meine Lösung liest das Bild als ‘.mat‘-Datei ein und man erhält eine Matrix in der Auflösungsgröße 1080x1920 Pixeln in der der Dreidimensionalen Matrix mit RGB Werte, also eine Matrix der Größe 1080x1920x3 [Siehe Abbildung 1]

Nach Ermittlung meiner gewählten Klassifizierungsmerkmale Flächeninhalt und gesamte Kantenlänge übergibt die Funktion ‘PartIdent‘ diese zusammen mit der Farbe des Bauteils als RGB Mittelwert als Rückgabewerte. Der weitere Algorithmus gleicht nun die Kriterien des Flächeninhalts und der Kantenlänge mit einer Dummy-Teileliste ab, in welcher Teilenummer, Flächeninhalt und Kantenlänge hinterlegt sind.
Falls sowohl die Fläche als auch die Kantenlänge in Pixeln innerhalb eines vorgegebenen aber einstellbaren Toleranzbereichs übereinstimmt, wird dem erkannten Teil seine dazugehörige Teilenummer zugeteilt und der Zählstand um eins erhöht.

Vorverarbeitung des Bildes

Das eingelesene Bild wird in Matlab mit der Funktion ‘rgb2grey‘ in ein Graustufenbild umgewandelt. Da das Originalbild wegen des strukturierten Förderband bei einer Schwellwertsegmentierung zu extremen Rauschen führen würde, wurde das Bild mittels des Gaußfilters anschließend weichgezeichnet.
Zur Umwandlung des Grauwertbildes in ein Schwarz-Weiß Bild, in dem das Bauteil selbst in weiß dargestellt und somit als binäre Zahl Eins hinterlegt ist, wurde eine histogrammbasierte Segmentierung angewendet.

%% Umwandlung des eingelesenen Bilder in Graustufen bzw Schwarz-Weiß Bild sowie Kontrasterhöhung
Bauteil_grau = rgb2gray(Bauteil_raw);   % Umwandlung des RGB Bildes in ein Graustufenbild
colormap gray                           % Definition des Farbraumes zu Grau    

Bauteil_grau_gauss = imgaussfilt(Bauteil_grau,2.5);         % Anwendung des Gaußfilters auf das Graustufenbild
se=(strel('square',1));                                     % strel erstellt ein Sturkturelement, welches für die morphologische Darstellung benätigt wird
Bauteil_gg_glatt= imopen(Bauteil_grau_gauss, se);           % Morphologischer Öffnungsoperator des Bilders
Schwellwert = 110;                                          % Mittels imageSegmenter ermittelter Schwellwert der Graustufen (Muss für andere Teile ggf. angepasst werden)
                                                            % ACHTUNG: Schwellwert beträgt 110 zur Erkennung der ausgewählten Teile!!! Nicht ändern    
nSchwellTeil = (Bauteil_gg_glatt < Schwellwert);            % Bildabfrage auf den Graustufenschwellwert erstellt binäres Bild
Bauteil_BW_HistSeg = ~nSchwellTeil;                         % Invertierung des Binären Bildes für die einfache Weiterverarbeitung

Um im Anschluss mithilfe der Bildsegmentierung von Matlab mehrere Teile in einem Bild zu erkennen und zu segmentieren, ohne dabei Fehleinflüsse von einfallendem Licht am Rand des Bildes in Kauf zu nehmen, wird ein in der Breite definierbarer schwarzer Rand in das Bild eingezeichnet

%% Erzeugung eines schwarzen Rahmens um Artefakte von möglichem Lichteinfall vorzubeugen
Randbreite = 100;                                           % Randbreite des schwarzen Außenrandes in Pixeln (muss ggf. auf neue Umgebungen angepasst werden)
Bauteil_BW_HistSeg(1 : Randbreite , 1 : Randbreite) = 0;    % Befüllen mit 0 (= shwarz) der pixel 0 bis Randbreite
Bauteil_BW_HistSeg(size(Bauteil_BW_HistSeg,1)-Randbreite : size(Bauteil_BW_HistSeg,1) ...
,size(Bauteil_BW_HistSeg,2)-Randbreite : size(Bauteil_BW_HistSeg,2)) = 0;

Die Anzahl der weißen Pixel entspricht nach diesem Schritt dem Flächeninhalt des Bauteils, welcher das erste große Erkennungsmerkmal meiner Klassifizierungsmethode darstellt.
Zusätzlich zum Flächeninhalt, welcher von vielen Teilen sehr ähnlich ausfallen kann, wurde zur Klassifizierung die Gesamtlänge der Kanten einbezogen. Zum erhalten der Kanten wurde das Sobelfilter eingesetzt, welches auf das Schwarz-Weiß Bild angewendet wurde und somit keinerlei Probleme bei der Erkennung der Kanten aufweist. Auch die gesamte Kantenlänge ist lediglich die Summe aller Pixel des Sobel gefilterten Bildes. Für meinen Algorithmus ist eine Erkennung und ein nachfolgender Abgleich der Farbe nicht notwendig, da es meine Bauteile im EV3 Kasten nur in einer Farbe gibt. Nichtsdestotrotz habe ich eine Farberkennung in der Funktion ‘PartIdent‘ implementiert, sodass die Funktion die Farbe als [R G B] Array zurückgibt. Sie beinhaltet den RGB Durchschnittswert aller Bildpunkte des Bauteils. Realisiert wurde dies über eine schwarze Bildmaske, die mit dem Originalbild multipliziert wurde und somit die RGB Werte des Hintergrundes zu Null setzt und die des Bauteils beibehält.

        % % Farberkennung schnellere Version und Rückgabe der Durchschnittswerte in [R G B]
        % % Durchschnittsdauer zur Teileerkennung pro Teil: ~0.5897 Sekunden
        FarbMaske = im2uint8(FarbMaske);                                    % Umwandlung des "RGB" Bildes in ein uint8 Bild für spätere Multiplikation                     
        clear RGB_Array R G B                                               % Löschen der Variablen, um eine Doppelbelegung durch vorhergehende Teile zu vermeiden
        colormap gray
        RGB_Array(:,:,1) = (Bauteil_raw(:,:,1).*FarbMaske(:,:,1));          % Punktoperator Multiplikation um den RGB Farbbereich R nur im nicht schwarzen Bereich beizubehalten
        RGB_Array(:,:,2) = (Bauteil_raw(:,:,2).*FarbMaske(:,:,1));          % Punktoperator Multiplikation um den RGB Farbbereich G nur im nicht schwarzen Bereich beizubehalten
        RGB_Array(:,:,3) = (Bauteil_raw(:,:,3).*FarbMaske(:,:,1));          % Punktoperator Multiplikation um den RGB Farbbereich B nur im nicht schwarzen Bereich beizubehalten
        R = RGB_Array(:,:,1);                                               % Zuweisung des RGB Farbbereichs Rot zu R
        G = RGB_Array(:,:,2);                                               % Zuweisung des RGB Farbbereichs Grün zu G
        B = RGB_Array(:,:,3);                                               % Zuweisung des RGB Farbbereichs Blau zu B
        R = R(R > 0);                                                       % Entfernen aller Nullwerte durch Abfrage auf größer 0
        G = G(G > 0);                                                       % Entfernen aller Nullwerte durch Abfrage auf größer 0
        B = B(B > 0);                                                       % Entfernen aller Nullwerte durch Abfrage auf größer 0
 
        RGB(n,:) = [round(mean(R),0) round(mean(G),0) round(mean(B),0)];    % Runden der R G und B Werte auf Null Nachkommastellen und Übergabe an das Rückgabearray RGB

Im Anschluss an den Funktionsdurchlauf werden sechs Stationen im Bildverarbeitungsprozess visuell in einer figure ausgegeben [siehe Abbildung 3]

Abbildung 3: Schematische Funktionserklärung

























Rückgabewerte der Funktion PartIdent

  • BauteilID: Array mit 2 Spalten und variabler Zeilenanzahl je nach Anzahl der gefunden Teile im Bild. Hinterlegt sind Flächeninhalt (Spalte 1) sowie gesamten Kantenlänge (Spalte 2) des Bauteils.
  • Farbe: Dreispaltiges Array mit den durchschnittlichen Farbwerten im RGB Farbbereich [R G B]

Die Farbe wurde in meinem Algorithmus nicht weiter verwendet. Es wird lediglich am Ende angezeigt, welche RGB Werte das Bauteil hat.Somit könnte eine weitere Einschränkung des erkannten Bauteils mit der Farbe einfach realisiert und implementiert werden. Dieses weitere Kriterium war in meinem Fall nicht Notwendig, da Flächeninhalt sowie Kantenlänge eindeutig sind und die Bauteile im EV3 Baukasten nur jeweils in einer Farbe vorhanden sind.

Auszug aus dem Matlab Code im Detail

Im Folgenden wird der Teil des Skriptes beschrieben, welcher die übergebenen von der Funktion übergebenen Daten mit der vorgegebenen Dummy-Legoteileliste abgleicht

       for k = 1 : TeileAnzahl
            PartPosi_logic = ((DatenAbgleich(:,2) - DatenAbgleich(:,2)*Toleranz <= BauteilID(k,1)) ...  % Abgleich des Flächeninhaltes und der Kantenlänge  
            & (BauteilID(k,1) <= DatenAbgleich(:,2) + DatenAbgleich(:,2)*Toleranz)) ...                 % mit den Hinterlegten Daten/Klassifizierung.
            & ((DatenAbgleich(:,3) - DatenAbgleich(:,3)*Toleranz <= BauteilID(k,2)) ...                 % Findet, falls vorhanden, das übereinstimmende Teil
            & (BauteilID(k,2) < DatenAbgleich(:,3) + DatenAbgleich(:,3)*Toleranz));                     % und hinterlegt die Position logisch im Array PartPosi_logic
            
            PartPosi = find(PartPosi_logic == 1);                                   % Findet die Positionen im PartPosi_logic Array, an denen alle Bedingungen übereinstimmten                                
            if ~isempty(PartPosi)                                                   % Falls das Array PartPosi nicht leer ist = Flächeninhalt und Kantenlänge konnten einem Bauteil zugewiesen werden
                PartNumber = DatenAbgleich(PartPosi,1);                             % Zuweisung der Teilenummer zum gefundenen Teil nach Klassifizierungsmermalen (Fläche und Kantenlänge)
                BereitsVorhanden = find(Zaehlliste_Kasten(:,1) == PartNumber,1);    % Kontrolliert ob bereits ein Teil selber Bauart in der Liste ist, um ggf nur die Anzahl zu erhöhen
                FremdteilBereitsVorhanden = find(FremdTeile(:,1) == PartNumber,1);  % Kontrolliert, ob das Teil bereits im FremdteileArray vorhanden ist
            else
                fprintf('\n Das Bauteil konnte nicht identifiziert werden. Es wird aussortiert.');
            end

Den gesamten Matlab Code findet man im SVN Projektordner DSB, zu finden unter dem Link welcher im Kapitel „Siehe auch“ hinterlegt wurde.

Video

Das Video des Funktionsnachweises wurde als ‘.AVI‘ – Datei im SVN Ordner zum Projekt hochgeladen und wird in naher Zukunft auf der Plattform YouTube bereitgestellt.

Selbstreflexion

In meiner Lösung wurde die Erkennung von sechs verschiedenen Teilen Problemlos umgesetzt. Auch die Erkennung mehrere Teile in einem Bild wurde mittels Objektsegmentierung realisiert um somit gegebenenfalls mehrere Teile einzeln zu erkennen, die auf einem Bild erfasst wurden. Das könnte passieren, falls die mechanische Vereinzelung der Anlage Probleme haben sollte.
Der Algorithmus wurde so allgemein wie möglich geschrieben und ich habe stets im Hinterkopf gehabt, zukünftig so viel Mehrwert wie möglich für das Projekt der Legoteil Zählmaschine im SDE Praktikum zu erhalten. Dafür wurde zum Beispiel eine Dummy-Legoteilliste implementiert, welche im späteren Projektverlauf durch Auswählbare Listen ersetzt wird, welche den Inhalt des zu zählenden Legosortiments beinhalten. Diese Liste wird in meinem Algorithmus mit den gezählten Teilen abgeglichen und, falls vorhanden, eine Liste mit Fremdteilen sowie eine Liste mit den fehlenden Teilen im Sortiment mit Teilenummer und Anzahl ausgegeben. Um im späteren Projekt eine Echtzeit Verarbeitung realisieren zu können, wurde der erste Farberkennungsalgorithmus überarbeitet und durch eine schnellere Version ersetzt. Die durchschnittliche Identifizierungszeit von einem Bauteil konnte somit von ~1.25 Sekunden auf ~0.58 Sekunden verringert werden. Es wurden beide Varianten in der Funktion PartIdent beibehalten, wobei die langsamere Variante auskommentiert wurde.
Die Farberkennung wurde nur als weiteres Kriterium implementiert, sie wird für meine Lösung nicht benötigt und könnte somit zu Gunsten der Laufzeit auch komplett entfallen.

Siehe auch

  1. SVN: Checkout URL | https://svn.hshl.de/svn/DSB/trunk
  2. Wilhelm Burger, Mark James Burge.: Digitale Bildverarbeitung. Heidelberg: Springer Verlag, 2015. ISBN 978-3-642-04603-2 ISBN 978-3-642-04604-9 (eBook)
  3. Tönnies, K. D.: Grundlagen der Bildverarbeitung. München: Pearson Studium, 2005. ISBN 3-8273-7155-4
  4. Mathworks: Image Segmentation URL

Weblinks


→ zurück zum Hauptartikel: Digitale Signal- und Bildverarbeitung SoSe2015