|
Antwort |
Registriert seit: 7. Dez 2005 Ort: Würzburg 54 Beiträge |
#1
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:
...hier kommt jetzt die Hough-Transformation: ein Bresenham-Algo für den Kreis kommt hier zum Einsatz:
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;
Delphi-Quellcode:
...dann wieder ein wenig Zeug...
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;
Delphi-Quellcode:
...hier werden die Konturen ausgelesen und für jeden Punkt wird der Kreis (hier werden 2 Kreise) gezeichnet...
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;
Delphi-Quellcode:
...dann werden die besten Werte ausgelesen und in weiteren Punkten zwischengespeichert...
// 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;
Delphi-Quellcode:
...hier werden nun die gewicheteten Mittel-Punkte berechnet...// 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;
Delphi-Quellcode:
...und schließlich werden die Punkte nochmal gefiltert und dann entgültig gespeichert...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;
Delphi-Quellcode:
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).
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; 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
Wissen ist Macht. Das ändert aber so gut wie nichts an der Übermacht der Dummheit.
|
Zitat |
Registriert seit: 7. Dez 2005 Ort: Würzburg 54 Beiträge |
#2
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.
|
Zitat |
Registriert seit: 7. Dez 2005 Ort: Würzburg 54 Beiträge |
#3
...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
Wissen ist Macht. Das ändert aber so gut wie nichts an der Übermacht der Dummheit.
|
Zitat |
Ansicht |
Linear-Darstellung |
Zur Hybrid-Darstellung wechseln |
Zur Baum-Darstellung wechseln |
ForumregelnEs 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
|
|
Nützliche Links |
Heutige Beiträge |
Sitemap |
Suchen |
Code-Library |
Wer ist online |
Alle Foren als gelesen markieren |
Gehe zu... |
LinkBack |
LinkBack URL |
About LinkBacks |