Die Microsoft Kinect ist eine Kamera, welche für die Spielkonsole Xbox entwickelt wurde. Das Besondere an dieser Kamera ist ihre Möglichkeit, neben einem normalen RGB Bild, ein Tiefenbild ihrer Umgebung zurückzugeben. Damit wird es möglich komplexere Computer Vision Aufgaben zu implementieren. Darüber hinaus bietet das sog. Microsoft Kinect SDK die Möglichkeit das Skelett einer Person zu erkennen. Durch das "Skeleton-Tracking" ist es möglich festzustellen, wo die Gelenke einer Person sind und welche Gesten und Bewegungen sie ausführt. Leider ist dieses SDK nur für Windows erhältlich und wir arbeiten daher vor allem mit dem Tiefenbild der Kinect Kamera.
...
Danach muss eine Referenz auf das Kinect Objekt gesetzt werden:
Code Block |
---|
KinectKinect2 kinect; |
Im setup()
wird das Objekt dann initialisiert:
Code Block |
---|
void setup() {
kinectkinect2 = new Kinect(this);
kinectkinect2.initDevice();
} |
Wird eine Kinect Kamera v2 v1 verwendet, muss die Referenz und Initialisierung entsprechend angepasst werden:
Code Block |
---|
Kinect2Kinect kinect; // ohne "2"
void setup() {
kinect = new Kinect2Kinect(this);
kinect.initDevice();
} |
Sind die obigen Schritte erfolgt kann im Weiteren direkt auf die Informationen der Kinect zugegriffen werden. Die Library macht uns fünf Informationen zugänglich:
PImage (RGB)
der Kinect Videokamera.PImage (grayscale)
der Kinect IR Kamera.PImage (grayscale)
ein Tiefenbild, in dem die Helligkeit der Pixel für den Abstand steht (Heller = näher).PImage (RGB)
ein Tiefenbild, bei dem die Farbe für die Entfernung steht. int[] (array)
ein Array, welches die "Rohdaten" der Entfernung beinhaltet (0-2048 v1 und 0-4500 v2).
RGB Bild
Um das Tiefenbild der Kamera auszulesen kann die gleiche Methode verwendet werden, wie sie schon bei einer normalen Webcam zu Einsatz kam
Video Bild
Zuerst muss der Video-Stream der Kamera in der setup()
Funktion aktiviert werden:
Code Block |
---|
kinect.initVideo();
kinect.initDevice(); |
Dann kann das aktuelle Bild gelesen werden:
Code Block |
---|
PImage videoImage = kinect.getVideoImage(); |
...
Neben dem zeichnen des Bildes in der draw()
Funktion, gibt es auch die Möglichkeit, das Bild asynchron zu zeichnen. Das bedeutet, es wird nur dann ein Bild gezeichnet, wenn ein neues vorhanden ist. Die entsprechende Funktion lautet:
Code Block |
---|
void videoEvent(Kinect k) {
// There has been a video event!
} |
IR Bild
...
Code Block |
---|
title | Beispiel |
---|
collapse | true |
---|
|
import org.openkinect.processing.*;
Kinect2 kinect;
void setup() {
size(1920, 1080);
kinect = new Kinect2(this);
kinect.initVideo();
kinect.initDevice();
}
void draw() {
image(kinect.getVideoImage(), 0, 0);
} |
Infrarot Bild
Zuerst muss der Infrarot-Stream der Kamera in der setup()
Funktion aktiviert werden:
Code Block |
---|
kinect.enablIRinitIR(true); |
...
Dann kann das aktuelle Bild gelesen werden:
Code Block |
---|
PImage videoImageirImage = kinect.getVideoImage(getIrImage(); |
Code Block |
---|
title | Beispiel |
---|
collapse | true |
---|
|
import org.openkinect.processing.*;
Kinect2 kinect;
void setup() {
size(512, 424);
PImage
irImagekinect = new Kinect2(this);
kinect.initIR();
kinect.initDevice();
}
void draw() {
image(kinect.getIrImage(), 0, 0);
} |
Tiefenbild
Zuerst muss der Tiefenbild-Stream der Kamera in der setup()
Funktion aktiviert werden:
Code Block |
---|
kinect.initDepth();
kinect.initDevice(); |
Um das Tiefenbild als Graustufen zurückzubekommen wird folgendes gemacht:
Code Block |
---|
PImage depthImage = kinect.getDepthImage(); |
Gezeichnet wird es mit:
Code Block |
---|
image(depthImage, 0, 0); |
Code Block |
---|
title | Beispiel |
---|
collapse | true |
---|
|
import org.openkinect.freenect.*;
import org.openkinect.freenect2.*;
import org.openkinect.processing.*;
Kinect2 kinect2kinect;
void setup() {
size(512, 424, P2D);
kinect2kinect = new Kinect2(this);
kinect2kinect.initDepth();
kinect2kinect.initDevice();
}
void draw() {
background(0);
image(kinect2kinect.getDepthImage(), 0, 0);
println(kinect2.depthWidth +" "+ kinect2.depthHeight);
} |
Tiefenarray
Der Nachteil des Graustufen-Tiefenbildes ist es, dass alle Entfernungsinformationen auf Werte zwischen 0 bis 255 (Helligkeit) verteilt werden. Tatsächlich kann die Kinect Kamera aber eine viel genauere Auflösung der Tiefeninformationen liefern. Dazu ist es jedoch nötig, das Tiefenarray aufzurufen und auszulesen. Dies geschieht über:
...
Code Block |
---|
title | Beispiel |
---|
collapse | true |
---|
|
import org.openkinect.processing.*;
import peasy.*;
Kinect2 kinect2kinect;
PeasyCam cam;
void setup() {
size(5121920, 4241080, P3D);
cam = new PeasyCam(this, 1000);
cam.setMaximumDistance(5000);
kinect2kinect = new Kinect2(this);
kinect2kinect.initDepth();
kinect2kinect.initDevice();
}
void draw() {
background(0);
int[] depthMap = kinect2kinect.getRawDepth();
int stepSize = 2;
for(int x=0; x<kinect2x<kinect.depthWidth; x+=stepSize) {
for(int y=0; y<kinect2y<kinect.depthHeight; y+=stepSize) {
int loc = x+y*kinect2kinect.depthWidth;
PVector point = depthToPointCloudPos(x, y, depthMap[loc]);
stroke(255);
point(point.x, point.y, point.z);
}
}
}
PVector depthToPointCloudPos(int x, int y, float depthValue) {
PVector point = new PVector();
point.z = (depthValue);
point.x = (x - CameraParams.cx) * point.z / CameraParams.fx;
point.y = (y - CameraParams.cy) * point.z / CameraParams.fy;
return point;
}
static class CameraParams {
static float cx = 254.878f;
static float cy = 205.395f;
static float fx = 365.456f;
static float fy = 365.456f;
static float k1 = 0.0905474;
static float k2 = -0.26819;
static float k3 = 0.0950862;
static float p1 = 0.0;
static float p2 = 0.0;
} |
...
Code Block |
---|
title | Beispiel |
---|
collapse | true |
---|
|
import org.openkinect.freenectprocessing.*;
import org.openkinect.freenect2.*;
Kinect2 kinect;
void setup() {
size(512, 424);
kinect = new Kinect2(this);
kinect.initRegistered();
kinect.initDevice();
}
void draw() {
image(kinect.getRegisteredImage(), 0, 0);
} |
Übersicht der Funktionen
Hier eine Übersicht der Funktionen, welche die Library zur Verfügung stellt:
initVideo()
— Video Bild initialisieren initIR()
—Infrarot Bild initialisiereninitDepth()
— Tiefenbild initialisieren
initDevice()
— Gerät initialisierenPImage getVideoImage()
— RGB BildPImage getIrImage()
— IR BildPImage getDepthImage()
— TiefenbildPImage getRegisteredImage()
— Angeglichenes RGB Bildint[] getRawDepth()
— Tiefenarray
Für weitere Informationen: the javadoc reference.
Weitere Beispiele
Code Block |
---|
title | Kinect Test |
---|
collapse | true |
---|
|
import org.openkinect.processing.*;
Kinect2 kinect2kinect;
void setup() {
size(5121024, 424, P2D848);
kinect2kinect = new Kinect2(this);
kinect.initVideo();
kinect.initDepth();
kinect2kinect.initRegisteredinitIR();
kinect.initRegistered();
kinect2kinect.initDevice();
}
void draw() {
background(0);
image(kinect2.getRegisteredImage(), 0, 0kinect.getVideoImage(), 0, 0, kinect.depthWidth, kinect.depthHeight);
image(kinect.getDepthImage(), kinect.depthWidth, 0);
image(kinect.getIrImage(), 0, kinect.depthHeight);
println(kinect2image(kinect.getRegisteredImage(), kinect.depthWidth +" "+ kinect2.depthHeight, kinect.depthHeight);
fill(255);
text("Framerate: " + (int)(frameRate), 10, 515);
} |
Übersicht der Funktionen
Hier eine Übersicht der Funktionen, welche die Library zur Verfügung stellt:
initDevice()
— Alles initialisieren (RGB, IR, Tiefenbild)initVideo()
— Nur das RGB Bild initialisieren initDepth()
— Nur das Tiefenbild initialisierenactivateDevice(int)
– Aktivieren eines speziellen Gerätes (bei mehreren Kinects an einem Rechner)enableIR(boolean)
— Infrarotbild aktivieren/deaktivieren (nur Kinect v1)enableColorDepth(boolean)
— Tiefeninformationen als Farbwert/GrauwertenableMirror(boolean)
— Bild spiegeln (nur Kinect v1)PImage getVideoImage()
— RGB Bild (oder IR Bild für Kinect v1)PImage getIrImage()
— IR Bild (nur Kinect v2)PImage getDepthImage()
— TiefenbildPImage getRegisteredImage()
— Angeglichenes RGB Bild (nur Kinect v2)int[] getRawDepth()
— Tiefenarray
...
Code Block |
---|
title | Depth Threshold |
---|
collapse | true |
---|
|
import org.openkinect.processing.*;
Kinect2 kinect;
int lowerThreshold = 0;
int upperThreshold = 750;
void setup() {
size(512, 424);
kinect = new Kinect2(this);
kinect.initDepth();
kinect.initDevice();
}
void draw() {
PImage img = kinect.getDepthImage();
int[] depthMap = kinect.getRawDepth();
loadPixels();
for (int x = 0; x < kinect.depthWidth; x++) {
for (int y = 0; y < kinect.depthHeight; y++) {
int loc = x+ y * kinect.depthWidth;
int rawDepth = depthMap[loc];
if (rawDepth > lowerThreshold && rawDepth < upperThreshold) {
pixels[loc] = color(150, 50, 50);
} else {
pixels[loc] = img.pixels[loc];
}
}
}
updatePixels();
} |
Code Block |
---|
title | Background Removal and mass centre |
---|
collapse | true |
---|
|
import org.openkinect.processing.*;
Kinect2 kinect;
int[] depthMapRef;
int[] depthMap;
int Threshold = 30;
int depthlength;
// Raw location
PVector loc = new PVector(0, 0);
// Interpolated location
PVector lerpedLoc = new PVector(0, 0);
// Depth data
int[] depth;
void setup() {
size(640, 520);
depthlength = 640*480;
kinect = new Kinect2(this);
kinect.initDepth();
kinect.initDevice();
depthMapRef = new int[depthlength];
}
void draw() {
float sumX = 0;
float sumZ = 0;
float sumY = 0;
float count = 0;
fill(255, 255, 255, 1);
rect(0, 0, width, height);
PImage img = kinect.getDepthImage();
depthMap = kinect.getRawDepth();
if (depthMapRef == null) {
arrayCopy(depthMap, depthMapRef);
}
img.loadPixels();
for (int x = 0; x < kinect.depthWidth; x++) {
for (int y = 0; y < kinect.depthHeight; y++) {
int loc = x+ y * kinect.depthWidth;
int difference = abs(depthMap[loc]-depthMapRef[loc]);
if ( difference > Threshold) {
sumX += x;
sumY += y;
sumZ += depthMap[loc];
count++;
img.pixels[loc] = color(150, 50, 50);
} else {
img.pixels[loc] = img.pixels[loc];
}
}
}
img.updatePixels();
image(img, 0, 0);
// find the average location of the "body mass"
if (count != 0) {
loc = new PVector(sumX/count, sumY/count, sumZ/count);
}
// Interpolating the location with lerp for a smoother animation
lerpedLoc.x = PApplet.lerp(lerpedLoc.x, loc.x, 0.3f);
lerpedLoc.y = PApplet.lerp(lerpedLoc.y, loc.y, 0.3f);
lerpedLoc.z = PApplet.lerp(lerpedLoc.z, loc.z, 0.3f);
fill(255);
text("depth:" +lerpedLoc.z+"", lerpedLoc.x+5, lerpedLoc.y);
ellipse(lerpedLoc.x, lerpedLoc.y, 5, 5);
}
void mousePressed() {
background(255);
arrayCopy(depthMap, depthMapRef);
}
|