AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Code-Bibliothek Neuen Beitrag zur Code-Library hinzufügen Delphi Fingertip Detection für Microsoft Kinect unter Delphi 7
Thema durchsuchen
Ansicht
Themen-Optionen

Fingertip Detection für Microsoft Kinect unter Delphi 7

Ein Thema von ich2 · begonnen am 27. Dez 2010 · letzter Beitrag vom 16. Feb 2011
Antwort Antwort
Benutzerbild von ich2
ich2

Registriert seit: 7. Dez 2005
Ort: Würzburg
54 Beiträge
 
#1

Fingertip Detection für Microsoft Kinect unter Delphi 7

  Alt 27. Dez 2010, 23:11
Hallo zusammen,

seitdem Microsoft vor einigen Wochen seine neue controllerlose Steuerung auf den Markt gebracht hat, ist so einiges passiert:
es hat nur 2 Wochen gedauert, bis der erste Open-Source-Treiber da war:
http://codelaboratories.com/nui

von da an wurde schon einige spektakuläre Videos gezeigt:
http://kinecthacks.net

...ich konnte nicht widerstehen und habe mir auch eine solche 3D-Kamera zugelegt und auch gleich ein wenig damit herumgespielt...

wenn erstmal die Treiber installiert sind, dann kann man recht einfach auf die Kamera zugreifen:
http://files.codes-sources.com

(Datei im Anhang: UKinect.pas)
Delphi-Quellcode:
...

type
  CLNUIMotor = record
    handle: ^Integer;
  end;

  CLNUICamera = record
    handle: ^Integer;
  end;

  CLNUIDepth = record
    table: array [ 0..640, 0..480 ] of ^Byte;
  end;

  CLNUIcolor = record
    table: array [ 0..640, 0..480 ] of ^Byte;
  end;

  TAcceleroMeter = packed record
    X, Y, Z: Short;
  end;

var
  KinectHandle: CLNUIMotor;
  KinectSerial: String;
  KinectCameraHandle: CLNUICamera;

  function CreateNUIMotor (): CLNUIMotor; cdecl; external 'CLNUIDevice.dll';
  function DestroyNUIMotor ( _handle: CLNUIMotor ): Boolean; cdecl; external 'CLNUIDevice.dll';
  function GetNUIMotorSerial ( _handle: CLNUIMotor ): PAnsiChar; cdecl; external 'CLNUIDevice.dll';
  function SetNUIMotorPosition ( _handle: CLNUIMotor; _value: Integer ): Boolean; cdecl; external 'CLNUIDevice.dll';
  function GetNUIMotorAccelerometer ( _handle: CLNUIMotor; var value_X: short; var value_Y: short; var value_Z: short ): Boolean; cdecl; external 'CLNUIDevice.dll';
  function SetNUIMotorLED ( _handle: CLNUIMotor; value_Led: Byte ): Boolean; cdecl; external 'CLNUIDevice.dll';

  function CreateNUICamera (): CLNUICamera; cdecl; external 'CLNUIDevice.dll';
  function DestroyNUICamera ( _handle: CLNUICamera ): Boolean; cdecl; external 'CLNUIDevice.dll';
  function StartNUICamera ( _handle: CLNUICamera ): Boolean; cdecl; external 'CLNUIDevice.dll';
  function StopNUICamera ( _handle: CLNUICamera ): Boolean; cdecl; external 'CLNUIDevice.dll';
  function GetNUICameraDepthFrameRAW ( _handle: CLNUICamera; var pData: CLNUIDepth; waitTimeout: integer = 2000 ): Boolean; cdecl; external 'CLNUIDevice.dll';
  function GetNUICameraDepthFrameRGB32 ( _handle: CLNUICamera; var pData: CLNUIDepth; waitTimeout: integer = 2000 ): Boolean; cdecl; external 'CLNUIDevice.dll';
  function GetNUICameraColorFrameRAW ( _handle: CLNUICamera; var pData: CLNUIcolor; waitTimeout: integer = 2000 ): Boolean; cdecl; external 'CLNUIDevice.dll';
  function GetNUICameraColorFrameRGB24 ( _handle: CLNUICamera; var pData: CLNUIcolor; waitTimeout: integer = 2000 ): Boolean; cdecl; external 'CLNUIDevice.dll';
  function GetNUICameraColorFrameRGB32 ( _handle: CLNUICamera; var pData: CLNUIcolor; waitTimeout: integer = 2000 ): Boolean; cdecl; external 'CLNUIDevice.dll';

...

...mich interessierte es nun, wie man auf 'einfache' Art und Weise eine Hand-Detection bzw. Finger-Detection realisiert.
ich möchte hier einmal meine Idee vorstellen und hoffe auf weitere Anregungen:

Im Grunde wird einem das Schwerste von der Kamera selbst abgenommen: das Herausfiltern der Hand!

Man bekommt von der Kinect-Kamera 2 Daten-Streams: einmal ein 'normales' WebCam-Bild und einmal das dazugehörige Tiefen-Bild (bild1)

Mit Hilfe der Tiefen-kodierung kann man sich die Hand herausfiltern (hier gehe ich davon aus, dass die hand vom Körper nach vorne gestreckt wird). Als Ergebnis bekommt man die Hand ans sich (bild2).

Als nächstes möchte ich nur die Konturen der Hand haben. Dafür lasse ich einen Laplace-Filter drüberlaufen (Faltung mit einem 3x3-Filterkern). das Ergebnis sind die Konturen der Hand (bild3).

So weit so gut...um nun die Finger oder besser ersteinmal die Hand zu detektieren, verwende ich die Hough-Transformation für Kreise. Das Transformierte Bild sieht dann für die Fingererkennung etwa folgendermaßen aus: (bild4). Es wird um jeden Punkt der Hand-Kontur ein Kreis mit einem bestimmten Radius gezeichnet. In dem sogenannten Akkumulatorraum werden die Schnittpunkte aller Kreise ausgewertet und mit einem Schwellwert ausgelesen.

Die gesammelten Punkte sind aber noch zu ungenau und werden mit einem quasi k-means-Algorithmus gewichet und weiter eingegrenzt.

Für die Handerkennung geht man ähnlich vor, man verwendet um Grunde nur einen anderen Radius für die Hough-Transformation.

...hier mal eine kleine Übersicht:
also erstmal ein paar Definitionen:
Delphi-Quellcode:
procedure findfinger3 ( const bmpSrc: TPicture; th, r, th2, th3, th4: Integer );
type
  TMeanPoints = packed record
    vec: TVector2D;
    count: Integer;
    value: Real;
  end;
  TMeanPoints_Type = array of TMeanPoints;

  TAkkumulator = packed record
    v1: SmallInt;
  end;

var
  counter, counter2, _th, _r, X, Y, Color: Integer;
  LigS, LigD: pLigScan;
  _Akkumulator: array [ 1..640, 1..480 ] of TAkkumulator;
  points: array of TVector2D;
  points_count: Integer;
  meanpoints: TMeanPoints_Type;
  meanpoints_count: Integer;
  test: Boolean;
  d, t: real;
  phi: SmallInt;
...hier kommt jetzt die Hough-Transformation: ein Bresenham-Algo für den Kreis kommt hier zum Einsatz:

Delphi-Quellcode:
  procedure Add_Akkumulator ( _X, _Y, _R, _v: Integer );
  var
    x_, y_, d, dx, dxy: Integer;
  begin
    x_ := 0;
    y_ := _r;
    d := 1 - _r;
    dx := 3;
    dxy := -2 * _r + 5;

    while ( y_ >= x_ ) do begin

      inc ( _Akkumulator [ _X + x_, _Y + y_ ].v1, _v );
      inc ( _Akkumulator [ _X + y_, _Y + x_ ].v1, _v );
      inc ( _Akkumulator [ _X + y_, _Y - x_ ].v1, _v );
      inc ( _Akkumulator [ _X + x_, _Y - y_ ].v1, _v );
      inc ( _Akkumulator [ _X - x_, _Y - y_ ].v1, _v );
      inc ( _Akkumulator [ _X - y_, _Y - x_ ].v1, _v );
      inc ( _Akkumulator [ _X - y_, _Y + x_ ].v1, _v );
      inc ( _Akkumulator [ _X - x_, _Y + y_ ].v1, _v );

      if ( d < 0 ) then begin
        d := d + dx;
        dx := dx + 2;
        dxy := dxy + 2;
        inc ( x_ )
      end else begin
        d := d + dxy;
        dx := dx + 2;
        dxy := dxy + 4;
        inc ( x_ );
        dec ( y_ );
      end;
    end;
  end;
...dann wieder ein wenig Zeug...

Delphi-Quellcode:
begin

  points_count := 0;
  _r := r;
  _th := th;

  if bmpSrc = nil then
    EXIT;

  try
    for X := 1 to 640 do
      for y := 1 to 480 do
        _Akkumulator [ X, Y ].v1 := 0;
...hier werden die Konturen ausgelesen und für jeden Punkt wird der Kreis (hier werden 2 Kreise) gezeichnet...

Delphi-Quellcode:
    // collect all points
    for Y := _r + 1 to bmpSrc.height - _r - 2 do begin
      LigS := bmpSrc.Bitmap.ScanLine [ Y ];
      for X := _r + 1 to bmpSrc.width - _r - 2 do begin

        if LigS [ X ].rgbtBlue > 0 then begin

          Add_Akkumulator ( X, Y, _r, 5 );
          Add_Akkumulator ( X, Y, _r-1, 5 );
        end;

      end;
    end;
...dann werden die besten Werte ausgelesen und in weiteren Punkten zwischengespeichert...

Delphi-Quellcode:

    // get the maxima
    for Y := 1 to 480 do //160 do
      for X := 1 to 640 do begin

        if ( ( _Akkumulator [ X, Y ].v1 > _th ) ) then begin

          inc ( points_count );
          setlength ( points, points_count );
          points [ points_count - 1 ] := Vector2D ( X, Y );
          if form1.CheckBox4.Checked then
            Form1.Image3.Canvas.Pixels [ X, Y ] := _Akkumulator [ X, Y ].v1;
        end else if _Akkumulator [ X, Y ].v1 <= -1 then begin
          Form1.Image3.Canvas.Pixels [ X, Y ] := clWhite;
        end;

      end;
...hier werden nun die gewicheteten Mittel-Punkte berechnet...

Delphi-Quellcode:

    t := th2 / 10;

    // compute the mean-points
    meanpoints_count := 0;
    meanpoints := nil;
    for counter := 1 to points_count do begin
      test := false;
      for counter2 := 1 to meanpoints_count do begin
        d := Get_Distance ( points [ counter - 1 ], meanpoints [ counter2 - 1 ].vec );
        if d < t{1.4*r} then begin
          inc ( meanpoints [ counter2 - 1 ].count );
          meanpoints [ counter2 - 1 ].value := meanpoints [ counter2 - 1 ].value + d;
          meanpoints [ counter2 - 1 ].vec := Add_Vector ( meanpoints [ counter2 - 1 ].vec, Scale_Vector ( Sub_Vector ( points [ counter - 1 ], meanpoints [ counter2 - 1 ].vec ), 1 / meanpoints [ counter2 - 1 ].count ) );
          test := true;
          break;
        end
      end;

      if not test then begin
        inc ( meanpoints_count );
        setlength ( meanpoints, meanpoints_count );
        meanpoints [ meanpoints_count - 1 ].vec := points [ counter - 1 ];
        meanpoints [ meanpoints_count - 1 ].count := 1;
        meanpoints [ meanpoints_count - 1 ].value := 0;
      end;

    end;
...und schließlich werden die Punkte nochmal gefiltert und dann entgültig gespeichert...

Delphi-Quellcode:
    x := th3;
    d := th4/10;

    r := _r;
    for counter := 1 to meanpoints_count do begin
      if ( ( ( meanpoints [ counter - 1 ].value/meanpoints [ counter - 1 ].count ) < d ) and ( meanpoints [ counter - 1 ].count > x ) ) then begin

        inc ( hands_count );
        setlength ( hands, hands_count );
        hands [ hands_count - 1 ].coords := meanpoints [ counter - 1 ].vec;
        hands [ hands_count - 1 ].value := round ( meanpoints [ counter - 1 ].value/meanpoints [ counter - 1 ].count );
      end;

    end;
  except
    exit;
  end;
end;
Auf weiteren Bildern (bilder) kann man bereits erkennen, dass die Hände schon relativ gut erkannt werden und auch der Status 'Hand offen' und 'Hand geschlossen' erkennbar ist (weiße und rote Einrahmung).
Die Finger können dann 'fast' genau erkannt werden...da muss noch ein wenig verbessert werden.

...im Anhang ist das fertig kompilierte Projekt für Delphi7 zu finden...

viel Spaß damit

Grüße
Angehängte Dateien
Dateityp: rar kinect5.rar (416,7 KB, 94x aufgerufen)
Wissen ist Macht. Das ändert aber so gut wie nichts an der Übermacht der Dummheit.
  Mit Zitat antworten Zitat
Benutzerbild von ich2
ich2

Registriert seit: 7. Dez 2005
Ort: Würzburg
54 Beiträge
 
#2

Update: Fingertip Detection für Microsoft Kinect unter Delphi 7

  Alt 9. Jan 2011, 18:54
hallo zusammen...

es ist ein wenig Zeit vergangen und ich habe mir weitere Gedanken zur simplen Hand und Finger Erkennung unter Delphi gemacht.
Das alte Konzept habe ich aus Geschwindigkeitsproblemen über den Haufen geworfen.

Die neue Idee funktioniert folgendermaßen:
Nach einer Tiefenfilterung des 3D-Bildes der Kinect-Kamera, lasse ich einen Thinning-Algorithmus drüber laufen. Als Resultat
bleibt ein Skelett meiner Hand und der Finger übrig. Die Endpunkte und die Triplepunkte werden herausgefiltert und markiert.
Nach einer k-means Analyse können die Hand und die Finger erkannt werden.
Der Algorithmus läuft wesentlich schneller als die alte Variante und ist auch robuster bei der Erkennung.

Wie gut das mittlerweile funktioniert, kann hier angeschaut werden:
hand and fingertip detection with Delphi
hand and fingertip detection with Delphi - App Control

Wer Interesse hat an dem source code, der soll sich einfach melden...

mfg
Wissen ist Macht. Das ändert aber so gut wie nichts an der Übermacht der Dummheit.
  Mit Zitat antworten Zitat
Benutzerbild von ich2
ich2

Registriert seit: 7. Dez 2005
Ort: Würzburg
54 Beiträge
 
#3

AW: Fingertip Detection für Microsoft Kinect unter Delphi 7 -- Update

  Alt 16. Feb 2011, 15:14
...ein kleines Update...


Hallo zusammen...

Der kinect-hype scheint sich ja ein wenig zu beruhigen, aber dennoch findet man immer wieder sehr interessante Anwendungen für dieses lustige Spielzeug.
Auch wir haben noch ein wenig weiter experimentiert und die Kinect Midi fähig gemacht.

Wir haben damit eine Lichtanlage in einer Diskothek gesteuert. Als Lichtsteuerungs-Software haben wir die Software Move-it 4 verwendet:
Youtube: Delphi Kinect - App Control II

Der Algorithmus für die Echtzeit Hand- und Fingererkennung ist soweit gleich geblieben (s.o.).

Da wir einige Anfragen zum Quellcode bekommen haben, kommt dieser nun...

---kinect6.rar

hier haben wir einmal angefangen ein wenig im Quellcode aufzuräumen, aber dennoch ist es sehr 'proof of principle'
-> mehr in der readme
-> beinhaltet die verwendeten Treiber von Codelaboratories (CL-NUI-Platform-1.0.0.1121) -- die neueren Treiber funktionieren nicht!!

---kinect2midi.rar

dieses Paket ist die Erweitertung des kinect6-Quellcodes (nicht aufgeräumt). Für die Midi-Anbindung kommt die PionoEx-Komponente zum Einsatz (enthalten).
-> es sind keine Treiber in diesem Paket enthalten

happy coding
Angehängte Dateien
Dateityp: rar kinect6.rar (1,16 MB, 120x aufgerufen)
Dateityp: rar kinect2midi.rar (1.021,0 KB, 80x aufgerufen)
Wissen ist Macht. Das ändert aber so gut wie nichts an der Übermacht der Dummheit.
  Mit Zitat antworten Zitat
Antwort Antwort


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 07:53 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz