Benutzer-Werkzeuge

Webseiten-Werkzeuge


Seitenleiste

projektesose20:gestensteuerung1public:start

Gestensteuerung


Themenbeschreibung/Einleitung

Unsere Idee ist es ein Programm zu schreiben mit dem Ziel Echtzeit-Bilder aus einen Computer Webcam aufzunehmen, die Gesten zu erkennen und daraufhin bestimmte Programme bzw. Applikationen auf den Rechner auszuführen. Dabei ist besonders zu achten, dass unser Programm 3~5 von uns bestimmten Gesten erkennt, diese richtig auf ihren Aufgaben zuordnen und die vorbestimmten Aufgaben ausführen können.Wir haben unser Projekt in drei großen Themenbereichen eingeteilt und zwar in die Bildverarbeitung, die Bilderkennung und die Aufgabenausführung.

Umsetzung/Funktionalität

Überblick

Ich habe mich hauptsächlich mit der Bildverarbeitung beschäftigt und versucht Bilder aus der Webcam für die Bilderkennung in den späteren Phasen vorzubereiten. Dabei habe ich hauptsächlich die OpenCV library(eine Programmbibliothek mit Algorithmen für die Bildverarbeitung und Computer Vision) benutzt, um Bildrauschen und andere Störfaktoren im Bild zu eliminieren. 1

Abbildung 1: Ablauf

Arbeitsabschnitte

Die Vorbereitung auf die Bildverarbeitung wird in sechs verschiedene Schritten aufgeteilt.

  1. Das Video wird vom Webcam eingelesen
  2. Das Video wird in Greyscale konvertiert
  3. Anwendung der Gauß-Filter
  4. Schwellenwertverfahren (Thresholding)
  5. Dilation und Erosion
  6. Konturen erkennen

Das Video wird zuerst vom Webcam eingelesen (Abb.2 links) und in Greyscale konvertiert, denn mit Grauwerten kann man Bildelemente w.z.B. die Kanten und Lichtverhältnisse von bestimmten Objekten erfassen, ohne die Farben einzubeziehen, was sonst den Prozess verkompliziert.2

Abbildung 2: Links: Normales Bild aus der Webcam, Rechts: Bild nach greyscale konvertiert


Mit einem Gauß-Filter wird Bildrauschen reduziert, indem kleinere Strukturen verloren gehen und gröbere Strukturen erhalten bleiben. 3


Abbildung 3: Nach Anwendung von Gauß-Filter


Hier haben wir ein Schwellenwert festgelegt (hier im Beispiel 0.3). Alle Pixeln über den Schwellenwert nehmen die Farbwerte von Weiß an und alle unter den Schwellenwert nehmen die Werte von Schwarz an, somit erkennen wir ein klarer Umriss, der uns bzw. den Rechner erleichtert das Objekt wahrzunehmen. 4


Abbildung 4: Nach Anwendung von Schwellenwertverfahren


Durch die Anwendung vom Schwellenwert entstehen einzelne Stücke von Umrissen, deswegen wird eine Dilation-Funktion angewendet, um die weißen Pixeln zu erweitern bzw. die einzelnen Stücken von Umrissen zu verbinden. Auf einem Grauwertbild wirkt die Dilatation mit einem strukturierenden Element ähnlich einem Maximum-Filter. Helle Strukturen werden vergrößert, dunklere verkleinert.
Die Erosion-Funktion ist das Gegenteil von dilation. Auf einem Grauwertbild funktioniert die Erosion mit einem strukturierenden Element ähnlich einem Minimum-Filter. Dunkle Strukturen werden vergrößert, hellere verkleinert. 5


Abbildung 5: Nach Anwendung von dilate(links) und erode(rechts)


Im letzten Schritt werden die Konturen mit Hilfe des "Canny edge detector"-Algorithmus berechnet. 6


Abbildung 6: Nach Anwendung von edgedetection


Unser Ergebnis muss noch optimiert werden, da Lichtverhältnisse und Hintergrund immer noch einen großen Einfluss hat auf die Erkennung von Konturen. Die oben dargestellten Beispiele wurden in einen dunklen Raum mit einen einzigen Lichtquelle, die direkt auf die Handfläche strahlte, aufgezeichnet.

Code

Im Processing gibt es eine „Video“-library, mit der man auf die Webcam zugreifen kann. Für das Projekt habe ich zusätzlich die OpenCV library (eine Programmbibliothek mit Algorithmen für die Bildverarbeitung und Computer Vision) benutzt, um Bildrauschen und andere Störfaktoren im Bild zu eliminieren.

import processing.video.*;
import gab.opencv.*;
Capture video;
PImage prev;
OpenCV opencv;
float threshold = 25;

Hier wählen wir uns eine Kamera aus, mit der das Video aufgenommen wird und mit video.start(); beginnen wir mit der Aufnahme. Ein Bild(prev) wird aufgenommen, das später als Hintergrund fungiert.

void setup () {
  size(640, 480);
  String[] cameras = Capture.list();
  printArray(cameras);
  video = new Capture(this, cameras[0]);
  opencv = new OpenCV(this, width, height);
  video.start();
  prev = createImage(640, 480, RGB);         //create a blank image
}

Zusätzlich werden zwei Bilder (curr und result) erschaffen. curr steht für die jetzigen (current) Live-Aufnahme. Mit .filter(GRAY) werden die Pixeln in beide Bilder(curr und prev)zu Grayscale (Grauwerte) konvergiert. Um die veränderte Werte wiederzugeben wird .loadPixels() verwendet.

void draw() {
  PImage result =  createImage(640, 480, RGB);
  PImage curr = createImage(640, 480, RGB);
  curr.copy(video, 0, 0, video.width, video.height, 0, 0, prev.width, prev.height);  
  prev.filter(GRAY);
  curr.filter(GRAY);
 
  result.loadPixels();
  prev.loadPixels();
  curr.loadPixels();

Durch einen for-loop werden die Differenzen der einzelne Pixel zwischen den Hintergrund (prev) und die Live-Aufnahme (curr) berechnet. Ist der Unterschied größer ist als der festgelegte threshold(Schwellenwert), so werden sie in Bild result eingesetzt und ausgelesen.

  for (int i = 0; i < prev.pixels.length; i++) {
    int difference = curr.pixels[i] - prev.pixels[i];
    if (difference > 25) {
      result.pixels[i] = difference;
    }
  }

In diesem Abschnitt benutzen wir diverse Filter, die Processing zur Verfügung stellt, um zuerst Bildrauschen zu entfernen. Danach wird es in Binärbild verändert und zum Schluss durch Erosion weiterverarbeitet. Mit der findCannyedges-Funktion aus OpenCV werden die Kanten berechnet und gezeichnet.

  result.updatePixels();
  result.filter(BLUR, 3);
  result.filter(THRESHOLD, 0.3);
  result.filter(ERODE);
  image(result, 0, 0);
  opencv.loadImage(result);
  opencv.findCannyEdges(20, 75); // Kantendetektion
  PImage edges = opencv.getSnapshot();
  image(edges, 0, 0);
}

Mit einen Mausklick werden die Pixelwerte in dem Moment gespeichert und als Hintergrund (prev) festgelegt.

//press mouse to "set the background"
void mousePressed() {
  prev.copy(video, 0, 0, video.width, video.height, 0, 0, prev.width, prev.height);     //copy the whole video into previous image(blank image we created) 
  prev.updatePixels();
}

Hier ist der Befehl, um die Bilder aus der Kamera auf dem Bildschirm wiederzugeben.

//read image from Camera
void captureEvent(Capture video) {
  video.read();
}

Fortsetzung

Unserer Ursprünglicher Ansatz eine neuronale Netzwerk zu trainieren, um die Gesten zu erkennen war leider nicht optimal für die Aufgabe, deswegen haben wir uns für eine andere Methode entschieden, wo wir für die Erkennung von Gesten die Berechnung dessen Konvexen Hülle anwenden.
Eine konvexe Hülle einer Mengen von Punkten ist das kleinste konvexe Polynom, welches alle Punkte enthält.Angenommen, die Punkte der Menge sind Nägel, die aus einem Brett hervor stehen, dann kann man mit einem Gummieband um alle Nägeln spannen und der kleinstmöglicher Umfang entspricht der Umrisslinie der konvexe Hülle Eine der Methode um die KH zu finden ist der Graham Scan. Dieser Algorithmus findet als aller erstes einen Punkt, dass garantiert auf der konvexen Hülle liegt z.B. der Punkt mit den kleinsten y-Koordinate und größte x-Koordinate.
Die Punkte werden dann nach dem Winkel, den sie mit dem Anfangspunkt machen sortiert. Wenn wir nun den Punkten gegen dem Uhrzeigersinn entlang gehen, können wir bei jedem Tripel entscheiden, ob der mittlere Punkt des Tripels einen Knick nach innen oder nach außen repräsentiert. Falls ein Knick nach innen zeigt, kann der mittlere Punkt nicht Teil der komplexen Hülle sein und wird ignoriert.
Wir betrachten die Kante zwischen den Linien und finden mit Kreuzprodukt heraus, ob es sich gegen die Uhrzeiger dreht, also nach außen. Dieser Prozess laufen wir durch alle Punkte.
Bereiche innerhalb des konvex Hulls, die aber nicht zur Handfläche gehören, werden convexity defects genannt. Durch diese können wir genau die Positionen der Fingerspitzen erkennen. Wir können dann den Winkel zwischen zwei convexity defects berechnen und je nach Winkelgröße bestimmen, ob ein Finger gestreckt wird oder nicht.
Link zu konvexe Hülle

Ergebnis/Diskussion

Durch Schwierigkeiten sowohl in der Teamarbeit als auch in der Umstellung unseres Lösungsansatzes kamen wir letztendlich nicht zum eigentlichen Ziel. Ich habe die Schritte bis zur Kantendetektion erreicht und die notwendigen Vorgänge zur Weiterentwicklung des Projekts herausgearbeitet. Leider war es mir nicht möglich am Ende diese noch in Code umzusetzen.
Es besteht noch Verbesserungsmöglichkeiten wie z.B. die Optimierung der Bildstabilität. Lichtverhältnisse und Hintergrund spielen weiterhin eine entscheidende Rolle, welche einen großen Einfluss hat auf die Erkennung von Konturen. Da unser Projekt leider nicht fertig geworden ist, gibt es noch viel Raum für die Weiterentwicklung.



Processing-Code:
gesture_code.pde.zip

projektesose20/gestensteuerung1public/start.txt · Zuletzt geändert: 2021/02/03 13:33 von d.golovko