====== Einleitung ====== {{ :projektesose18:rembrandt:rembrandt2.png?300|}} Unser Roboter ist nach Rembrandt van Rijn benannt, einem bekannten Künstlers des Barock. Er trug mit dazu bei, dass die Kunst in den Alltag der breiten Mittelschicht Einzug fand. In seinen frühen Anfängen entstand eine Reihe von Radierungen, in denen er sich experimentell mit verschiedenen Gesichtsausdrücken darstellte. Wir haben das als Motivation genommen einen Roboter zu bauen, der Bilder verarbeiten und dann die Kanten des Bildes nachzeichnen soll. Dazu muss der Roboter Bilder aufnehmen können, sie dann so bearbeiten, dass er die Kanten erkennt und diese dann auf ein Blatt Papier zeichnet. Zur Bildaufnahme benutzen wir eine Webcam und die Bildbearbeitung und -analyse läuft über einen angeschlossenen Computer. Der Roboter besteht aus zwei Wagen, die durch Steppermotoren bewegt werden. Durch die Ansteuerung dieser Motoren wird dann ein Bild gezeichnet. Zur Bedienung von Rembrandt 2.0 müssen Arduino und Webcam an den Computer angeschlossen und das Steckbrett über ein Netzteil mit 8V Spannung versorgt werden. Die Wagen sollten sich an der linken oberen Blattecke (aus Sicht des/der Portraitierten) befinden und an der Wäscheklammervorrichtung am oberen Wagen muss ein Filzstift befestigt werden, sodass die Spitze möglichst senkrecht auf dem Papier aufsitzt. Wird das Programm gestartet, erscheint ein Livebild der Webcam, sodass man Gelegenheit hat, den Bildausschnitt zu optimieren. Zum Starten muss lediglich die Enter-Taste gedrückt werden. Es erscheint das Kantenbild, welches der Roboter nun beginnt zu zeichnen. (Theoretisch sollte der Fortschritt dieses Prozesses über Einfärbung der bereits gezeichneten Pixel visualisiert werden. In der Realität kann man das Bild aber nur aktualisieren, indem man das Canvas-Fenster für einen Augenblick aus dem Bildschirmbereich hinauszieht.) Für Abbruch und Neustart kann die Entf.-Taste gedrückt werden. ====== Umsetzung ====== ===== Überblick ===== Die Aufgaben, die wir während unsere Projektphase zu erfüllen hatten, waren der Umbau des bestehenden MazeMate-Aufbaus, die Bildaufnahme und -bearbeitung, eine Extraktion der Zeichenbefehle aus dem bearbeiteten Bild und die Kommunikation zwischen Processing und Arduino. Das Programm an sich besteht aus sechs Teilen: Dem Hauptdokument ''rembrandt.pde'', mit den Unterprogrammen ''edgeDetection.pde'' für die Bildbearbeitung, ''sketchEdges.pde'' für die Extraktion der Zeichenbefehle, die dann über ''moveTo.pde'' an das Arduinoprogramm ''rembrandt.ino'' übertragen werden und ''signature.pde'', welches am Ende eine eigene Signatur hinzufügt. Im folgenden werden die einzelnen Aufgaben und Teilprogramme näher erläutert. ===== Umbau ===== Wir haben den Roboter MazeMate verwendet und ihn an unsere Anforderungen angepasst. Ursprünglich war noch ein Elektromagnet dabei, den haben wir ausgetauscht mit einer Vorrichtung aus Wäscheklammern, die einen Stift halten kann. Das Kernstück der Konstruktion ist der Wagen mit dem Stifthalter. Er besteht aus zwei prinzipiell baugleichen Ebenen. Sie werden durch Stepper-Motoren mit Zahnrädern auf Zahnschienen bewegt. Damit nicht das gesamte Gewicht des Wagens auf den Zahnrädern am Stepper liegt, laufen beide Teile auf jeweils vier Rädern. Um den Wagen geradlinig bewegen zu können, werden die Räder durch kleine Hölzer in der Spur gehalten. Dabei fährt die untere Ebene neben dem Zeichenblatt auf der Grundplatte und die obere um 90 Grad gedreht auf der unteren. Oben auf dem kleinen Teil des Wagens ist das Steckbrett mit der kompletten Schaltung des Roboters angebracht. Dazu gehören die beiden Stepper für die Bewegung und ein weiterer Stepper, der die Stifthalterung bewegt, so das der Stift auf- und abgesetzt wird. Auf der Grundplatte ist neben dem Zeichenblatt und dem Wagen noch Platz für einen Laptop. Dieser wird benötigt, da der Arduino nicht genug Rechenleistung für Bildverarbeitung bzw. Processing hat. Also ist der Laptop mit der Webcam und dem Arduino verbunden. Außerdem benötigt die Schaltung ein Netzteil oder einen Akku, da der Arduino nur mit 5V läuft, die Stepper jedoch 8V benötigen. Liste der Bauteile: - Holzplatte 50x80cm - 3x Kanthölzer à 50cm - 3x Winkel - Schrauben und Kabelbinder - 8x Rad mit 3cm Durchmesser - Holzplatte für untere Ebene 40x15cm - Holzplatte für obere Ebene 15x15cm - Hölzer zur Spurhaltung 200cm - Zahnschiene à 25cm - 3x Stepper - 2x Zahnrad - Steckbrett - Arduino Nano - 3x Treiber für Stepper - 3x Kondensator - Kabel - Webcam - Netzteil - Wäscheklammern - Holzplatte als Zeichenfläche 29x39cm - Zwei A4-Moosgummi-Blätter als weiche Unterlage - Filzstift und Papier ===Pins===
// SCHRITT 1:
for (all lines) {
for (all columns) {
//SCHRITT 2:
if (current pixel is on edge) {
// SCHRITT 2a: Stift zur Kante bewegen und absetzen
onEdge = true;
while (onEdge) {
onEdge = false;
// SCHRITT 2b:
// Liste mit Indizes der Nachbarpixel erstellen
int i = 0; // Nummer des Nachbarpixels
while (i<8) {
//SCHRITT 2c:
if (neighbors[i] is on edge) {
// Stift dorthin bewegen, aktuellen Pixel aus der Kante löschen, Nachbarpixel wird neuer aktueller Pixel
i=8; // Abbruchbedingung für die while-Schleife
onEdge = true;
}
}
i+=1;
}
}
// SCHRITT 2d: aktuellen Pixel von der Kante löschen, Stift hochnehmen
}
}
}
Da der Roboter unerwartet lange selbst für das Zeichnen eines nur kurzen Strichs benötigt, wurde der Algorithmus weiter ausgefeilt: Anstatt stets nur kurze Linien bei der Bewegung von aktuellem Pixel zum Nachbarpixel zu malen, sollen dort, wo die Kante gerade ist, gleich die gesamte Länge des Abschnittas auf einmal gemalt werden. In Abb. {{ref>sketchedges}} beispielsweise soll die lange horizontale Linie nicht schrittweise sondern in einem Zug aufs Papier gebracht werden.
Dies lässt sich mit einer weiteren ''if''-Bedingung und ein paar zusätzlichen Variablen realisieren: Hat der Algorithmus eine Kante gefunden, merkt er sich in jedem Durchlauf der äußeren ''while''-Schleife die vorherige und die aktuelle Bewegungsrichtung. Erst wenn sie nicht übereinstimmen, zeichnet er die Linie. Für horizontale oder vertikale Abschnitte wird der Roboter also erst eine Linie ziehen, wenn er am Ende des Abschnitts angelangt ist, wo die Kante einen Knick macht.
Die ''moveTo''-Befehle zur Bewegung des Stiftes werden im folgenden Abschnitt erläutert.
----
===== Kommunikation zwischen Processing und Arduino =====
Die ''moveTo''-Funktion übermittelt zwei Zahlen an den Arduino: Die erste gibt die Anzahl der Schritte und die Richtung an in die sich der Motor bewegen soll und die zweite Zahl gibt an welcher Motor bewegt werden soll.
Es gibt drei Motoren:
* um den Wagen vor und zurück fahren zu lassen
* um den Wagen nach links und nach rechts fahren zu lassen und
* um den Stift zu bewegen
Dazu muss die ''moveTo''-Funktion die erhaltenen Daten verarbeiten: einmal eine Pixelkoordinate und einmal ob der Stift, wenn er bewegt wird, auf- oder abgesetzt sein soll. Erst berechnet die Funktion, wie viele Schritte die einzelnen Motoren machen müssen und in welche Richtung. Danach wird wenn nötig der Stift bewegt. Wenn der Stift oben ist, dann werden die einzelnen Motoren nacheinander bewegt, wenn der Stift aufgesetzt ist, werden die Motoren in kleinen Schritten nacheinander bewegt, so das eine kleine Treppe entsteht, deren Stufen so klein sind, dass sie wie eine schräge Linie wirkt.
Schwierigkeiten haben wir damit, herauszufinden wie viel Pause man zwischen den einzelnen Bewegungen braucht, damit der Arduino nicht überfordert ist. Außerdem wissen wir nicht ob und welche Daten beim Arduino ankommen, was am Anfang auch zu Schwierigkeiten geführt hat.
Weitere Schwierigkeiten gab es bei der gezielten Ansteuerung der Motoren und das sie auch die richtige Anzahl an Schritten machen bzw. das das Verhältnis der Schritte stimmt, so dass wenn der Stift aufgesetzt ist eine Linie entsteht die zwei Punkte ziemlich genau verbindet.\\
Viele anfänliche Probleme konnten wir durch ausprobieren und durch austausch mit anderen Gruppen gelösen z.B. die Ansteuerung der Steppermotoren. Bisher ist bei den Steppermotoren noch das Problem, die Pausen besser anzupassen, damit das zeichen flüssiger funktioniert und trozdem keine Fehler auftreten. Dazu müsste man viele Tests durchführen und viel Ausprobieren.\\
Wir wissen war immer noch nicht was genau beim Arduino ankommt und was nicht aber wir wissen ob was ankommt, wenn die LED am arduino aufhört zu blinken.\\
Das größe Problem ist die kleinen Abweichungen der Motoren einzuschäzten und auszugleichen, nur dazu müssten eine Kontrolle eingebaut werden und dazu reicht die Zeit leider nicht mehr.
----
====== Diskussion ======
Im Grunde haben wir alle wichtigsten Ziele unseres Projektes erfüllt: Rembrandt 2.0 kann Fotos aufnehmen, darin die Kanten erkennen und bei einfachen Bildern diese auch halbwegs erkennbar aufs Papier übertragen. Er zeichnet dabei weitestgehend Linien und keine einzelnen Punkte wie bei einem Matrixdruck. Zudem hat er eine eigene Signatur, die er am Ende zum Bild hinzufügt. Bei komplexeren Bildern funktioniert er jedoch nicht einwandfrei. Dies ist auf mehrere Probleme zurückzuführen:
Im Teilprogramm ''edgeDetection'' funktioniert die Non-Maximum Suppression nicht hundertprozentig. Einige wenige Kanten der Bilder - vor allem in Bereichen großer Kontraste - bleiben breiter als ein Pixel. Das führt dazu, dass der Stift die Kante mehrmals hoch und runter fahren muss, bis sie vollständig gelöscht ist, was zu Ungenauigkeiten und teilweise auch zum Auslaufen des Stiftes führt. Grund dafür ist vermutlich, dass die mit Sobel berechneten Gradientenbeträge manchmal größer als 255 sind und dann einheitlich auf diesen Wert gesetzt werden. So entstehen größere Flächen mit weißen Pixeln, in denen die Non-Maximum Suppression dann kein Maximum finden kann.
Eine Idee wäre es, die Gradienten genauer zu berechnen, einerseits durch Nutzung des Satzes des Pythagoras statt Addition der Beträge (siehe Abb. {{ref>winkelbestimmung}}), andererseits durch ein Teilen durch 4, denn der maximal erreichbare Wert für den Gradienten beträgt 1020=4*255. Teilt man also durch 4, kann erreicht werden, dass die Gradientenwerte 255 nicht überschreiten und somit ein eindeutiges Maximum in einem Bereich mit hohen Gradienten errechnet werden kann.
Ein weiteres Problem der ''edgeDetection'' ist, dass manche Linien nicht durchgezogen sind. Durch häufiges Ab- und Aufsetzen des Stiftes kann es aber leicht zu Ungenauigkeiten kommen. Um dem vorzubeugen müsste anstelle des simplen Binärfilters am Ende ein stabiles Hystere-Verfahren implementiert werden. Dabei handelt es sich um eine Methode, die jeden Pixel anhand von zwei Schwellenwerten auf Zugehöigkeit zu einer Kante prüft. Er kann dabei nicht, vielleicht oder definitiv zu einer Kante gehören. Wenn er nur vielleicht zur Kante gehört, muss überprüft werden, ob er (direkt oder über andere vielleicht zur Kante gehörenden Pixel) mit einem definitiven Kantenpixel verbunden ist. Würden die Schwellenwerte gut gewählt, könnte erreicht werden, dass alle Kanten durchgezogen sind.
Dafür, aber auch für den Binärfilter wäre es zudem sinnvoll, die Schwellenwerte an die Helligkeit des Sobelbilds anzupassen, damit nicht zu viele aber auch nicht zu wenige Strukturen sichtbar sind. Momentan obliegt diese Optimierung noch dem Nutzer, z.B. durch Ausleuchtung des Raumes.
Hinzu kommt noch, dass es manchmal zu Kommunikationsaussetzern zwischen Arduino und Processing kommt. Wir vermuten, dass dies mit den Pausen zwischen den einzelnen Befehlen, die an den Arduino gesendet werden, liegt. Eine dynamische Anpassung der Pausen wäre also eine Möglichkeit, dieses Problem anzugehen.
Ein weiter Fehler tritt auf, wenn sich der Arduino in y-Richtung bewegen soll. Häufig legt er dabei nur die Hälfte des Weges zurück. Es könnte ein Motorfehler sein, da wir bei dem Code keinen Fehler gefunden haben.
Dadurch, das die Schreibunterlage nicht gerade ist ist die Druckstärke des Stiftes nicht konstant, kommt es zu kleinen Abweichungen im Bild: Mal ist die Reibung zwischen Stift und Papier so groß, dass für kleine Bewegungen die Kraft der Motoren nicht reicht, mal schwebt der Stift einen Millimeter über dem Papier. Das liegt aber vermutlich nicht nur an der Unterlage sondern auch an der Ungenauigkeit des Steppermotors, der den Stift steuert. Man müsste zwischendurch immer wieder kontrollieren ob der Stift in der Position ist, in der er sein soll, und/oder eine Stifthalterung bauen, die sich an den Druck anpassen kann. Dadurch würde es zu besseren Ergebnissen kommen. \\
Durch kleine Ungenauigkeiten der Motoren und dadurch, dass die Wägen ein wenig Spielraum haben, kommt es zu kleinen Fehlern. Wieder wäre durch eine Kontrolle während des Zeichnens eine Verbesserung des Ergebnisses möglich.
Bisher werden die Bilder Spiegelverkehrt gezeichnet, was auch dazu führt das die Unterschrift bisher über dem Bild Kopfüber steht. Durch Umstecken der Motoren oder der Änderung der Vorzeichen im Arduinoprogramm und dann kleinen entsprechenden Anpassungen könnte man dies ändern.
====== Code ======
{{:projektesose18:rembrandt:rembrandt.zip|}}
====== Quellen und Literatur ======
* Andreas Cobet: **Detektion und Erkennung von Text in Videos mit niedriger Qualität**. Doktorarbeit der Ingenieurswissenschaften, TU Berlin (2015), [[https://depositonce.tu-berlin.de/handle/11303/4994|Dokument]]
* Wikipedia (Hrsg.) **Canny Edge Detector**. [[https://en.wikipedia.org/wiki/Canny_edge_detector|Seite]], in der Version vom 23.05.2018 (letzter Zugriff: 15.07.2018, 22:36 Uhr)
* Processing Foundation (Hrsg.): **Processing Reference**. [[https://processing.org/reference/|Seite]] (lezter Zugriff: 15.07.2018, 22:35 Uhr)
* **MateMate-Projekt** aus dem vergangenen WiSe 2017/18, siehe: [[projektewise1718:matematepublic:start|Projektdokumentation]]