Archiv der Kategorie: kinect

Point Cloud


Processing 2.0

Der Kinect Sensor bietet die einzigartige Möglichkeit die Distanz der einzelnen Bildpunkte zur Kamera zu messen. Wir nutzen diese Möglichkeit jetzt und zeichnen alle einzelnen Bildpunkte, die der Sensor liefert einfach im 3-dimensionalen Raum darzustellen. Das ganze bezeichnet man als Point Cloud.

141104_113108_52

/** Copyright 2014 Thomas Koberger
 */

// https://lernprocessing.wordpress.com/
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import SimpleOpenNI.*;
import java.util.Calendar;

SimpleOpenNI kinect;

void setup() {
  size(1200, 768, P3D);
  kinect = new SimpleOpenNI(this);
  kinect.enableDepth();
  stroke(255);
}

void draw() {
  background(0);
  kinect.update();

  // Verschiebe die Szene in den Mittelpunkt des Fensters
  translate(width/2, height/2, -500);
  // vertikal drehen, damit die Szene nicht auf dem Kopf steht
  rotateX(radians(180));
  // Rotationspunkt in die Mitte der Szene verschieben

  translate(0, 0, 1500);
  randomSeed(20);
  // Damit die Szene automatisch rotiert.
  rotateY((float)frameCount/50);
  translate(0, 0, -1500);

  // Einkommentieren, wenn man die Szene per Maus rotieren will!
  //  rotateY(map(mouseX, 0, width, -PI, PI));
  //  rotateX(map(mouseY, 0, width, -PI, PI));

  // Hier liefert die Kinect ein Array mit Vektoren
  PVector[] depthPoints = kinect.depthMapRealWorld();

  // Wir zeichnen nicht jeden Punkt, um die Sache zu beschleunigen
  for (int i = 0; i < depthPoints.length; i+= (int)random(2, 10)) {
    PVector currentPoint = depthPoints[i];
    stroke(map(currentPoint.z, 0, 7000, 255, 80));
    point(currentPoint.x, currentPoint.y, currentPoint.z);
  }
}

void keyReleased() {
  if (key == DELETE || key == BACKSPACE) background(360);
  if (key == 's' || key == 'S') saveFrame(timestamp()+"_##.png");
} 

//timestamp
String timestamp() {
  Calendar now = Calendar.getInstance();
  println("Frame saved");
  return String.format("%1$ty%1$tm%1$td_%1$tH%1$tM%1$tS", now);
}

Farbe

Nun können wir auch beide Kameras der Kinect nutzen und den Punkten ihre entsprechende Farbe zuweisen. Dabei kommen die Tiefeninformationen von der IR und die Farbinformationen von der RGB Kamera. Die dafür notwendigen mathematischen Operationen, nämlich das Auffinden der korrespondierenden Punkte der beiden Kameras, erledigt die Kinect.

141107_164425_59

</pre>
<pre>/** Copyright 2014 Thomas Koberger
 */

// https://lernprocessing.wordpress.com/
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import SimpleOpenNI.*;
import java.util.Calendar;

SimpleOpenNI kinect;

void setup() {
  size(1200, 768, P3D);
  kinect = new SimpleOpenNI(this);
  kinect.enableDepth();
  // Aktiviert die RGB Kamera der Kinect
  kinect.enableRGB();
  //Berechnet die korresponierenden Punkte der beiden Kameras
  //Dadurch bekommen die korrespondierenden Punkte der Tiefen- und der RGB Kamera
  //die gleichen Indizes
  kinect.alternativeViewPointDepthToImage();
}

void draw() {
  background(0);
  kinect.update();

  // Verschiebe die Szene in den Mittelpunkt des Fensters
  translate(width/2, height/2, -500);
  // vertikal drehen, damit die Szene nicht auf dem Kopf steht
  rotateX(radians(180));
  // Rotationspunkt in die Mitte der Szene verschieben

  translate(0, 0, 1500);
 
  // Damit die Szene automatisch rotiert.
  rotateY((float)frameCount/50);
  translate(0, 0, -1500);

  // Einkommentieren, wenn man die Szene per Maus rotieren will!
  //  rotateY(map(mouseX, 0, width, -PI, PI));
  //  rotateX(map(mouseY, 0, width, -PI, PI));

  // Hier liefert die Kinect ein Array mit Vektoren
  PVector[] depthPoints = kinect.depthMapRealWorld();
  PImage rgbImage = kinect.rgbImage();
 
  // Wir zeichnen nicht jeden Punkt, um die Sache zu beschleunigen
  for (int i = 0; i < depthPoints.length; i+= 10) {
    PVector currentPoint = depthPoints[i];
    
    // Hiermit färben wir die Punkte der Tiefeninformation mit der entsprechend
    // Farbe aus der RGB Kamera ein.
    stroke(rgbImage.pixels[i],map(currentPoint.z,0,7000,255,80));
    point(currentPoint.x, currentPoint.y, currentPoint.z);
  }
  println(frameRate);
}

void keyReleased() {
  if (key == DELETE || key == BACKSPACE) background(360);
  if (key == 's' || key == 'S') saveFrame(timestamp()+"_##.png");
} 

//timestamp
String timestamp() {
  Calendar now = Calendar.getInstance();
  println("Frame saved");
  return String.format("%1$ty%1$tm%1$td_%1$tH%1$tM%1$tS", now);
}

Linien

Nun kann man die einzelnen Punkte auch mit Linien verbinden.

 

/** Copyright 2014 Thomas Koberger
 */

// https://lernprocessing.wordpress.com/
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import SimpleOpenNI.*;
import java.util.Calendar;

SimpleOpenNI kinect;

void setup() {
  size(1200, 768, P3D);
  kinect = new SimpleOpenNI(this);
  kinect.enableDepth();
  // Aktiviert die RGB Kamera der Kinect
  kinect.enableRGB();
  //Berechnet die korresponierenden Punkte der beiden Kameras
  //Dadurch bekommen die korrespondierenden Punkte der Tiefen- und der RGB Kamera
  //die gleichen Indizes
  kinect.alternativeViewPointDepthToImage();
  noSmooth();
}

void draw() {
  background(0);
  kinect.update();

  // Verschiebe die Szene in den Mittelpunkt des Fensters
  translate(width/2, height/2, -500);
  // vertikal drehen, damit die Szene nicht auf dem Kopf steht
  rotateX(radians(180));
  // Rotationspunkt in die Mitte der Szene verschieben

  translate(0, 0, 1500);

  // Damit die Szene automatisch rotiert.
  rotateY((float)frameCount/50);
  translate(0, 0, -1500);

  // Einkommentieren, wenn man die Szene per Maus rotieren will!
  //  rotateY(map(mouseX, 0, width, -PI, PI));
  //  rotateX(map(mouseY, 0, width, -PI, PI));

  // Hier liefert die Kinect ein Array mit Vektoren
  PVector[] depthPoints = kinect.depthMapRealWorld();
  PImage rgbImage = kinect.rgbImage();
  strokeWeight(1);
  PVector prevPoint, currentPoint;
  prevPoint = new PVector(0, 0, 0);
  // Wir zeichnen nicht jeden Punkt, um die Sache zu beschleunigen
  for (int i = 20; i < depthPoints.length; i+= 3) {

    currentPoint = depthPoints[i];

    stroke(rgbImage.pixels[i]); 

    if (prevPoint.x!=0 && currentPoint.x!=0) {
      line(currentPoint.x, currentPoint.y, currentPoint.z, prevPoint.x, prevPoint.y, prevPoint.z);
    }
    
    // Wie speichern die Koordinaten des aktuellen Punkts als letzten Punkt
    prevPoint = currentPoint;
  }
  println(frameRate);
}

void keyReleased() {
  if (key == DELETE || key == BACKSPACE) background(360);
  if (key == 's' || key == 'S') saveFrame(timestamp()+"_##.png");
} 

//timestamp
String timestamp() {
  Calendar now = Calendar.getInstance();
  println("Frame saved");
  return String.format("%1$ty%1$tm%1$td_%1$tH%1$tM%1$tS", now);
}

Advertisements

Kinect und Processing


Habe heute den Kinect Sensor bekommen. In Java kann man ihn mit verschiedenen Bibliotheken nutzen. Z.B. mit der Simple-OpenNI Bibliothek, welche die vielfältigsten Möglichkeiten bietet. Die Simple OpenNI ist ein OpenNI- und NITE Wrapper für Processing. Die Installation unter Win7 laut dieser Anleitung ist sehr einfach. Dann kann man den ersten Processing Code laufen lassen.

141103_145804_933
/** Copyright 2012 Thomas Koberger
 */
// https://lernprocessing.wordpress.com/
// Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import SimpleOpenNI.*;
import java.util.*;
SimpleOpenNI kinect;

void setup()
{
  size(640*2, 480);
  kinect = new SimpleOpenNI(this);
  kinect.enableDepth();
  kinect.enableIR();
}

void draw()
{
  kinect.update();
  image(kinect.depthImage(), 0, 0);
  image(kinect.irImage(), 640, 0);
  println(frameRate);
}

void keyReleased() {
  if (key == DELETE || key == BACKSPACE) background(360);
  if (key == 's' || key == 'S') saveFrame(timestamp()+&quot;_##.png&quot;);
}  


// timestamp
String timestamp() {
  Calendar now = Calendar.getInstance();
  return String.format(&quot;%1$ty%1$tm%1$td_%1$tH%1$tM%1$tS&quot;, now);
}


Was also kann der Kinect Sensor leisten?

Er liefert neben dem RGB Bild ein, durch einen IR-Emitter und einen IR-Sensor erzeugtes, Tiefenbild der Umgebung (eine DepthMap).  Das versetzt uns als Programmierer in die Lage, die Position von Objekten festzustellen und darauf zu reagieren. Der Kinect Sensor kann also die Lage von Objekten im Raum erkennen. Die Bilder haben eine Auflösung von 640 x 480 Pixel.

Die Technik hat allerdings noch ein paar Einschränkungen. Erstens funktioniert der Tiefensensor erst ab einer Distanz von ~50 cm. Zweitens werfen Objekte im Vordergrund natürlich einen Schatten. Für im Schatten liegende Objekte ist natürlich keine Tiefen-Information verfügbar.

Eine kleine Rolle in diesem Zusammenhang spielt bei diesen Überlegungen auch, dass die RGB- und Tiefenkamera in einem Abstand von einigen cm ihren Blick auf die Welt offenbaren.

Ubuntu 12.04 laut Anleitung installiert.

Kinect Einstieg


Die Kinect ist eine 3D Kamera. Sie nimmt in erster Linie nicht das Aussehen von Objekten auf, sondern deren Position im Raum.

Wie schafft die Kinect das?

Die Hardware besteht aus 2 Kameras. Einer normale RGB Kamera und einer Infrarot Kamera. Die RGB Kamera liefert ein Bild vergleichbar einer günstigen Webcam in einer Auflösung von 640 mal 480 Pixel. Die IR Kamera liefert ein IR Bild in der selben Auflösung. Außerdem ist die Kinect mit einem IR Projektor ausgestattet. Dieser projeziert Infrarot Pixel, die dann mit den entsprechenden Pixel der IR Kamera verrechnet werden. Daraus kann dann die Entfernung der Pixel vom Sensor errechnet werden.

121212_222007_369

IR Bild aus der Kinect

Die Kinect kann nun verschiedene Outputs produzieren. Dafür verwenden wir folgendes Beispielprogramm:

/** Copyright 2012 Thomas Koberger
*/

// https://lernprocessing.wordpress.com/
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
*
* MOUSE
*
* KEYS
* s                   : save png
*/

import SimpleOpenNI.*;
import java.util.Calendar;

SimpleOpenNI  kinect;

void setup()
{
size(640, 480);

kinect = new SimpleOpenNI(this);

// depthMap aktivieren
kinect.enableDepth();

// RGB Kamera aktivieren
//kinect.enableRGB();

// IR Kamera aktivieren
//kinect.enableIR();

// Scenemap aktivieren
//kinect.enableScene();
}

void draw()
{
// Update Kinect
kinect.update();

// Eine der vier Images können aktiviert werden, wenn auch in
// setup() die entsprechende Funktion aktiviert wurde
// Achtung: RGB und IR können nicht parallel genutzt werden!!!
image(kinect.depthImage(), 0, 0);
//image(kinect.rgbImage(),0,0);
//image(kinect.irImage(), 0, 0);
//image(kinect.sceneImage(),0,0);
}

void keyReleased() {
if (key == 's' || key == 'S') saveFrame(timestamp()+"_##.png");
}

// timestamp
String timestamp() {
Calendar now = Calendar.getInstance();
return String.format("%1$ty%1$tm%1$td_%1$tH%1$tM%1$tS", now);
}

Das Programm erzeugt zuerst einmal ein Objekt des SimpleOpenNI Sensors namens kinect. Mit dessen Hilfe kann man nun die Funktionen der Kinect kontrollieren. Damit wir dann Output generieren können, müssen wir die verschiedenen Funktionen der Kinect aktivieren. Das passiert in der Funktion setup(). In draw() müssen dann die Daten aus dem Sensor abergerufen werden. Nun können die vom Sensor bereitgestellten Bilder ausgegeben werden.

Wie sind diese Bilder zu interpretieren?

1. depthImage()

121212_221759_2334

Dieses Bild wird von der Kinect aus den Tiefeninformationen generiert. Nahe Objekte werden heller dargestellt, als weiter entfernte. Sind Objekte weniger weit als etwa 50 cm funktioniert die Entfernungsmessung nicht. Diese Objekte werden schwarz dargestellt. Der Messbereich reicht bis 8m. Die Entfernung werden von der Kinect sehr genau gemessen. Wir werden später sehen, wie wir diese ermitteln können. Was an  dem Bild noch auffällt ist, dass es kleine scharze Bereiche enthält. Diese werden von Schatten verursacht, die näher gelegene Objekte auf weiter entfernte werfen. In diesen Bereichen kann natürlich keine Tiefeninfo abgerufen werden.

Wenn wir das Programm um folgende Zeile erweitern, können wir die Tiefeninformation an der Mausposition abrufen. Dafür verwenden wir die OpenNI Funktion depthMap(). Sie gibt ein int- Array mit der Entfernung des Punktes zum Sensor zurück.

  println(kinect.depthMap()[640*mouseY+mouseX]);

2. rgbImage()

121212_221837_865

Dieses Bild stammt von der RGB Kamera und ist qualitativ nicht besonders ansprechend. Allerdings ist sie doch recht interessant. Wir werden uns später ansehen, wie man die Farbinfos auf die Tiefenprojektion übertragen und somit ein farbiges 3 dimensionales Bild erstellen können.

3. irImage()

121212_222007_369

Output der IR Kamera. Hier kann man sehr gut die projezierten IR Punkte sehen.

4. sceneImage()

121212_224833_15438

Hier sind wir schon bei den höheren Funktionen der SimpleOpenNI. In diesem Modus versucht die Library Gegenstände im Bild zu erkennen. Wird ein Gegenstand erkannt, färbt ihn das System automatisch ein.

Kinect Installation auf Ubuntu 12.04


Processing 2.0

Mit dem Kinect Sensor und Processing lassen sich wirklich tolle Sachen machen. Z.B.: 2 Arbeiten von Cedric Kiefer: unnamed-soundsculpture, magic story telling.

Buchtip:

Als guter Einstieg in englischer Sprache eignet sich : Making-Things-See.

Hardware:

Um die Kinect am Computer betreiben zu können braucht man eine externe Stromversorgung. Die gibt es auch im Bundle, welches ich auf die Schnelle im Amazon Webshop nicht mehr finden konnte. Es sollte aber nach wie vor verfügbar sein.

Installation:

Um unter Processing mit dem Kinect Sensor arbeiten zu können, braucht man einen Treiber inkl. Java/Processing wrapper. Hier gibt es im derzeit 2 Möglichkeiten.

  1. Den Treiber des OpenKinect Projekts mit einem Wrapper von Daniel Shiffman. Dieser funktioniert in der vorliegenden Version allerdings nur auf dem Mac.Er kann aber unter dieser Anleitung von Nikolaus Gradwohl auch für Linux kompiliert werden.
  2. Für alle Plattformen ist hingegen die simple-openni verfügbar. Die Installation unter Windows und Linux funktioniert lt. Anleitung. Einzig die Version des Treibers im Linux Paket geht aus der Anleitung nicht klar hervor. Hier muss die Version im /Ordner/kinect/….. installiert werden.Dann noch den Wrapper herunterladen und wie beschrieben im /libraries Ordner im Sketchbook entpacken und schon kanns mit dem Beipielcode von der Seite ans Testen gehen.Tipp: Auf Ubuntu 12.04 wird die Kinect nach dem Verbinden sofort von dem Programm Guvcview belegt. Damit das nicht passiert, muss die Datei /etc/modprobe.d/blacklist.conf als root mit der Zeile blacklist gspca_kinect angereichert werden.

import SimpleOpenNI.*;

SimpleOpenNI  context;

void setup()
{
context = new SimpleOpenNI(this);

// enable depthMap generation
context.enableDepth();

// enable camera image generation
context.enableRGB();

background(200,0,0);
size(context.depthWidth() + context.rgbWidth() + 10, context.rgbHeight());
}

void draw()
{
// update the cam
context.update();

// draw depthImageMap
image(context.depthImage(),0,0);

// draw camera
image(context.rgbImage(),context.depthWidth() + 10,0);
}

void keyReleased() {
//if (key == DELETE || key == BACKSPACE) background(360);
if (key == 's' || key == 'S') saveFrame(timestamp()+"_##.png");
}

// timestamp
String timestamp() {
Calendar now = Calendar.getInstance();
println("Frame saved");
return String.format("%1$ty%1$tm%1$td_%1$tH%1$tM%1$tS", now);
}