Fingerspitzenerkennung in EyesWeb

Aus toolbox_interaktion
Wechseln zu: Navigation, Suche

Bei einem interaktiven System möchte man oft auf eine konventionelle Steuerung (z.B. mit Hilfe einer Maus) zugunsten des interaktiven Aspekts weitestgehend verzichten. Eine Möglichkeit besteht darin, den Benutzer das System mit seinem Zeigefinger steuern zu lassen.

Dieser Artikel befasst sich mit der Erkennung einer Fingerspitze in Zusammenhang mit einer Aktionsfläche und einer Kamera, welche diese Aktionsfläche abfilmt.

Voraussetzungen

Allgemeine Voraussetzungen

  • Arbeitet man mit einer Projektion, welche auf die Aktionsfläche gerichtet ist, so muss man einen Infrarotfilter und Infrarotstrahler einsetzen, um die Projektion auszublenden.
  • Die Aktionsfläche muss vollständig von der Kamera erfasst werden. Um Aktionsfläche und Aufnahmefläche abzugleichen, kann man die Aktionsfläche zuerst freistellen.
  • Außer der Hand des Benutzers darf nichts anderes auf dem Kamerabild zu sehen sein.

Softwarevoraussetzungen

Idee

Um die Fingerspitze richtig erkennen zu können, gehen wir davon aus, dass sie den Pixel enthält, welcher am weitesten vom Schwerpunkt des ins Bild ragenden Armes entfernt ist. Da die Fingerspitze wenig Masse liefert, sollte sich der Schwerpunkt relativ nah am Bildrand befinden. In der Praxis zeigt sich diese Annahme aber als wenig robust, weshalb eine zweite Bedingung implementiert werden muss: Die Fingerspitze enthält nicht nur den Pixel, welcher am weitesten vom Schwerpunkt entfernt ist, sondern auch am nächsten zum Mittelpunkt des Videobildes liegt. Mit dieser zweiten Bedingung werden die Koordinaten der Fingerspitze äußerst robust erkannt.

EyesWeb Patch

EyesWeb-Patch
Der hier im Beispiel gezeigte EyesWeb-Patch fingerspitze.eyw wandelt das eingehende Videobild zuerst in ein Binärbild um, wobei noch einige Filter (Median, Opening) und eine Hintergrundsubtraktion eingebaut sind, um Störungen im Bild zu minimieren. Geeignete Kameratechnik vorausgesetzt, wird durch optimales Setzen des Binarisierungsschwellwertes der im Vergleich zum Hintergrund hellere Arm exakt aus dem Kamerabild herausgestellt. Anschließend kann aus diesem Binärbild der Schwerpunkt ermittelt werden. Dessen Koordinaten werden dann zusammen mit dem Grauwertbild an den selbst programmierten, auf OpenCV beruhenden, EyesWeb-Block (Abstand_2) weitergegeben (die Software zu diesem Block ist im internen Bereich als Datei abstand_2.zip abgelegt). Dieser wiederum schickt die ermittelten Koordinaten der Fingerspitze an einen OSC-Block bzw. gibt diese direkt aus.

EyesWeb-Block "Abstand"

Das Grundgerüst des EyesWeb-Blocks wird mit dem EyesWeb Wizard / EyesWeb Block Creator in Microsoft Visual C++ 6.0 erstellt.

EyesWeb Block Creator

Hierbei wird unter „Block Description“ festgelegt, dass es sich bei dem zu erstellenden Block um einen passiven Block handelt. Die Definition von externen Parametern ist nicht erforderlich.

Execute()

In die Execute()-Methode der Body-Datei (einfach zu finden über den Kommentar "//TODO: Add your implementation code here") wird die eigentliche Kernfunktionalität des Blocks eingefügt. Als erstes werden die Ein- und Ausgangskanäle gesperrt, damit deren Daten bearbeitet werden können.

IDTImage* pIn = (IDTImage*) LockInput(EYWIN_INPUTIMAGE);
IDTScalar* inX = (IDTScalar*) LockInput(EYWIN_INX);
IDTScalar* inY = (IDTScalar*) LockInput(EYWIN_INY);
IDTScalar* outX = (IDTScalar*) LockOutput(EYWOUT_OUTX);
IDTScalar* outY = (IDTScalar*) LockOutput(EYWOUT_OUTY);

Anschließend muss das „IDTImage“ in ein „IplImage“ umgewandelt werden. Dies ist nötig, denn die benutzte OpenCV-Funktion benötigt ein „IplImage“. Außerdem wird der Ursprung des Bildes auf links oben verschoben.

IplImage* in = NULL;
pIn->GetIplImage((void**) &in);
in->origin = 0;

Danach werden die X- und die Y-Koordinate des Schwerpunktes eingelesen und in zwei Variablen gespeichert. Außerdem wird der Mittelpunkt des Bildes berechnet, indem man die Breite und die Höhe jeweils durch die Zahl 2 teilt.

inX->GetIntValue((int*) &xSchwer);
inY->GetIntValue((int*) &ySchwer);
 
float mittelX = in->width/2;
float mittelY = in->height/2;

Anschließend folgt die if-/else-Bedingung, in der geprüft wird, welches Pixel am weitesten vom Schwerpunkt der Region und am nächsten zum Mittelpunkt des Bildes liegt. Um die Robustheit noch weiter zu erhöhen, wird die Lage des Schwerpunktes zusätzlich mit einbezogen. Und zwar wird das Bild zeilenweise von links oben beginnend durchlaufen, wenn der Schwerpunkt in der unteren Hälfte des Bildes liegt. Ist dem nicht so, wird das Bild zeilenweise von links unten beginnend durchlaufen. So wird annähernd sichergestellt, dass die Punkte am Rand nach den Punkten an der Fingerspitze untersucht werden.

if(ySchwer > mittelY)
 {
  for (y=0;y<in->height;y++)
   {
    for (x=0;x<in->width;x++)
	{
	if((unsigned char) *(in->imageData + in->widthStep * y + x))
		{
          curr_distance = sqrt((((int)xSchwer - x)*((int)xSchwer-x)) + (((int)ySchwer-y)*((int)ySchwer-y)));
 
	if(curr_distance > max_distance)
		   {
          curr_distanceM = sqrt((((int)mittelX - x)*((int)mittelX-x)) + (((int)mittelY-y)*((int)mittelY-y)));
 
	if(curr_distanceM < min_distanceM)
			{
			max_distance = curr_distance;
		     	min_distanceM = curr_distanceM;
 
			max_x = x;
			max_y = y;
			} } } } } }
else
{
 for (y=(in->height)-1;y>=0;y--)
   {
    for (x=in->width-1;x>=0;x--)
	{
	if((unsigned char) *(in->imageData + in->widthStep * y + x))
		{
          curr_distance = sqrt((((int)xSchwer - x)*((int)xSchwer-x)) +    (((int)ySchwer-y)*((int)ySchwer-y)));
 
	if(curr_distance > max_distance)
		   {
          curr_distanceM = sqrt((((int)mittelX - x)*((int)mittelX-x)) + (((int)mittelY-y)*((int)mittelY-y)));
 
	if(curr_distanceM < min_distanceM)
			{
			max_distance = curr_distance;
			min_distanceM = curr_distanceM;
 
			max_x = x;
			max_y = y;
			} } } } } }

Zum Schluss werden die gefundenen Koordinaten Variablen übergeben und die Input- und Outpuschnittstellen des Blocks wieder freigegeben.

outX->SetIntValue(max_x);
outY->SetIntValue(max_y);
 
UnlockInput(EYWIN_INPUTIMAGE);
UnlockInput(EYWIN_INX);
UnlockInput(EYWIN_INY);
UnlockOutput(EYWOUT_OUTX);
UnlockOutput(EYWOUT_OUTY);
 
return S_OK;

Die Programmierung des EyesWeb-Blocks wäre hiermit abgeschlossen. Nun kann die dll-Datei des Blocks erstellt und diese als Steuerelement registriert werden. Anschließend steht der Block in EyesWeb 3.x zur Verfügung.

Die Software zu diesem Block ist im internen Bereich als Datei abstand_2.zip abgelegt.