kinect/ processing / simple openni - point cloud data not being output properly - processing

I've created a processing sketch which saves each frame of point cloud data from the kinect to a text file, where each line of the file is a point (or vertex) that the kinect has registered. I plan to pull the data into a 3d program to visualize the animation in 3d space and apply various effects. The problem is, when I do this, the first frame seems proper, and the rest of the frames seem to be spitting out what looks like the first image, plus a bunch of random noise. This is my code, in its entirety. It requires simple openni to work properly. You can see the comments
import SimpleOpenNI.*;
//import processing.opengl.*;
SimpleOpenNI context;
float zoomF =0.5f;
float rotX = radians(180); // by default rotate the hole scene 180deg around the x-axis,
float rotY = radians(0); // the data from openni comes upside down
int maxZ = 2000;
Vector <Object> recording = new Vector<Object>();
boolean isRecording = false;
boolean canDraw = true;
boolean mouseMode = false;
int currentFile = 0;
int depthWidth = 640; //MH - assuming this is static?
int depthHeight = 480;
int steps = 5;
int arrayLength = (depthWidth/steps) * (depthHeight/steps); //total lines in each output file
void setup()
{
size(1024,768,P3D); // strange, get drawing error in the cameraFrustum if i use P3D, in opengl there is no problem
//size(1024,768,OPENGL);
context = new SimpleOpenNI(this);
context.setMirror(true);
depthWidth = context.depthWidth();
depthHeight = context.depthHeight();
// enable depthMap generation
if(context.enableDepth() == false)
{
println("Can't open the depthMap, maybe the camera is not connected!");
exit();
return;
}
stroke(255,255,255);
smooth();
perspective(radians(45),
float(width)/float(height),
10.0f,150000.0f);
}
void draw()
{
//println(isRecording);
// update the cam
context.update();
background(0,0,0);
// set the scene pos
translate(width/2, height/2, 0);
rotateX(rotX);
rotateY(rotY);
scale(zoomF);
// draw the 3d point depth map
int[] depthMap = context.depthMap();
int index = 0;
PVector realWorldPoint;
PVector[] frame = new PVector[arrayLength];
translate(0,0,-1000); // set the rotation center of the scene 1000 infront of the camera
stroke(200);
for(int y=0;y < context.depthHeight();y+=steps)
{
for(int x=0;x < context.depthWidth();x+=steps)
{
int offset = x + y * context.depthWidth();
realWorldPoint = context.depthMapRealWorld()[offset];
if (isRecording == true){
if (realWorldPoint.z < maxZ){
frame[index] = realWorldPoint;
} else {
frame[index] = new PVector(-0.0,-0.0,0.0);
}
index++;
} else {
if (realWorldPoint.z < maxZ){
if (canDraw == true){
point(realWorldPoint.x,realWorldPoint.y,realWorldPoint.z);
}
}
}
}
}
if (isRecording == true){
recording.add(frame);
}
if (mouseMode == true){
float rotVal = map (mouseX,0,1024,-1,1); //comment these out to disable mouse orientation
float rotValX = map (mouseY,0,768,2,4);
rotY = rotVal;
rotX = rotValX;
}
}
// -----------------------------------------------------------------
// Keyboard event
void keyPressed()
{
switch(key)
{
case ' ':
context.setMirror(!context.mirror());
break;
case 'm':
mouseMode = !mouseMode;
break;
case 'r':
isRecording = !isRecording;
break;
case 's':
if (isRecording == true){
isRecording = false;
canDraw = false;
println("Stopped Recording");
Enumeration e = recording.elements();
int i = 0;
while (e.hasMoreElements()) {
// Create one directory
boolean success = (new File("out"+currentFile)).mkdir();
PrintWriter output = createWriter("out"+currentFile+"/frame" + i++ +".txt");
PVector [] frame = (PVector []) e.nextElement();
for (int j = 0; j < frame.length; j++) {
output.println(j + ", " + frame[j].x + ", " + frame[j].y + ", " + frame[j].z );
}
output.flush(); // Write the remaining data
output.close();
//exit();
}
canDraw = true;
println("done recording");
}
currentFile++;
break;
}
switch(keyCode)
{
case LEFT:
if(keyEvent.isShiftDown())
maxZ -= 100;
else
rotY += 0.1f;
break;
case RIGHT:
if(keyEvent.isShiftDown())
maxZ += 100;
else
rotY -= 0.1f;
break;
case UP:
if(keyEvent.isShiftDown())
zoomF += 0.01f;
else
rotX += 0.1f;
break;
case DOWN:
if(keyEvent.isShiftDown())
{
zoomF -= 0.01f;
if(zoomF < 0.01)
zoomF = 0.01;
}
else
rotX -= 0.1f;
break;
}
}
I imagine the loop is where the problems begin occurring: for(int y=0;y < context.depthHeight();y+=steps)
{ , etc. although it could just be a problem with the python script I wrote for the 3d program. Anyway, this is a cool sketch, and I think would be super useful for anyone wanting to do 3d effects to point cloud data (or build models, etc), but I'm stuck at the moment. Thanks for your help!

Unfortunately I can't explain a lot right now, but I've sone something similar a few months back saving to PLY and CSV:
import processing.opengl.*;
import SimpleOpenNI.*;
SimpleOpenNI context;
float zoomF =0.5f;
float rotX = radians(180);
float rotY = radians(0);
boolean recording = false;
ArrayList<PVector> pts = new ArrayList<PVector>();//points for one frame
float minZ = 100,maxZ = 150;
void setup()
{
size(1024,768,OPENGL);
context = new SimpleOpenNI(this);
context.setMirror(false);
context.enableDepth();
context.enableScene();
stroke(255);
smooth();
perspective(95,float(width)/float(height), 10,150000);
}
void draw()
{
context.update();
background(0);
translate(width/2, height/2, 0);
rotateX(rotX);
rotateY(rotY);
scale(zoomF);
int[] depthMap = context.depthMap();
int[] sceneMap = context.sceneMap();
int steps = 10;
int index;
PVector realWorldPoint;
pts.clear();//reset points
translate(0,0,-1000);
//*
//stroke(100);
for(int y=0;y < context.depthHeight();y+=steps)
{
for(int x=0;x < context.depthWidth();x+=steps)
{
index = x + y * context.depthWidth();
if(depthMap[index] > 0)
{
realWorldPoint = context.depthMapRealWorld()[index];
if(realWorldPoint.z > minZ && realWorldPoint.z < maxZ){//if within range
stroke(0,255,0);
point(realWorldPoint.x,realWorldPoint.y,realWorldPoint.z);
pts.add(realWorldPoint.get());//store each point
}
}
}
}
if(recording){
savePLY(pts);//save to disk as PLY
saveCSV(pts);//save to disk as CSV
}
//*/
}
// -----------------------------------------------------------------
// Keyboard events
void keyPressed()
{
if(key == 'q') minZ += 10;
if(key == 'w') minZ -= 10;
if(key == 'a') maxZ += 10;
if(key == 's') maxZ -= 10;
switch(key)
{
case ' ':
context.setMirror(!context.mirror());
break;
case 'r':
recording = !recording;
break;
}
switch(keyCode)
{
case LEFT:
rotY += 0.1f;
break;
case RIGHT:
// zoom out
rotY -= 0.1f;
break;
case UP:
if(keyEvent.isShiftDown())
zoomF += 0.01f;
else
rotX += 0.1f;
break;
case DOWN:
if(keyEvent.isShiftDown())
{
zoomF -= 0.01f;
if(zoomF < 0.01)
zoomF = 0.01;
}
else
rotX -= 0.1f;
break;
}
}
void savePLY(ArrayList<PVector> pts){
String ply = "ply\n";
ply += "format ascii 1.0\n";
ply += "element vertex " + pts.size() + "\n";
ply += "property float x\n";
ply += "property float y\n";
ply += "property float z\n";
ply += "end_header\n";
for(PVector p : pts)ply += p.x + " " + p.y + " " + p.z + "\n";
saveStrings("frame_"+frameCount+".ply",ply.split("\n"));
}
void saveCSV(ArrayList<PVector> pts){
String csv = "x,y,z\n";
for(PVector p : pts) csv += p.x + "," + p.y + "," + p.z + "\n";
saveStrings("frame_"+frameCount+".csv",csv.split("\n"));
}
I'm using an if statement to save only the points within a certain Z threshold, but feel free to alter/use as you see fit.
The post processing idea reminds of the Moullinex video for Catalina. Check it out, it's well documented and includes source code as well.
Update
The posted code saves 1 file per frame. Even though the playback speed would be low, the sketch should still save a file for each frame. The code be simplified a bit:
import processing.opengl.*;
import SimpleOpenNI.*;
SimpleOpenNI context;
float zoomF =0.5f;
float rotX = radians(180);
float rotY = radians(0);
boolean recording = false;
String csv;
void setup()
{
size(1024,768,OPENGL);
context = new SimpleOpenNI(this);
context.setMirror(false);
context.enableDepth();
stroke(255);
smooth();
perspective(95,float(width)/float(height), 10,150000);
}
void draw()
{
csv = "x,y,z\n";//reset csv for this frame
context.update();
background(0);
translate(width/2, height/2, 0);
rotateX(rotX);
rotateY(rotY);
scale(zoomF);
int[] depthMap = context.depthMap();
int[] sceneMap = context.sceneMap();
int steps = 10;
int index;
PVector realWorldPoint;
translate(0,0,-1000);
//*
beginShape(POINTS);
for(int y=0;y < context.depthHeight();y+=steps)
{
for(int x=0;x < context.depthWidth();x+=steps)
{
index = x + y * context.depthWidth();
if(depthMap[index] > 0)
{
realWorldPoint = context.depthMapRealWorld()[index];
vertex(realWorldPoint.x,realWorldPoint.y,realWorldPoint.z);
if(recording) csv += realWorldPoint.x + "," + realWorldPoint.y + "," + realWorldPoint.z + "\n";
}
}
}
endShape();
if(recording) saveStrings("frame_"+frameCount+".csv",csv.split("\n"));
frame.setTitle((int)frameRate + " fps");
//*/
}
// -----------------------------------------------------------------
// Keyboard events
void keyPressed()
{
switch(key)
{
case ' ':
context.setMirror(!context.mirror());
break;
case 'r':
recording = !recording;
break;
}
switch(keyCode)
{
case LEFT:
rotY += 0.1f;
break;
case RIGHT:
// zoom out
rotY -= 0.1f;
break;
case UP:
if(keyEvent.isShiftDown())
zoomF += 0.01f;
else
rotX += 0.1f;
break;
case DOWN:
if(keyEvent.isShiftDown())
{
zoomF -= 0.01f;
if(zoomF < 0.01)
zoomF = 0.01;
}
else
rotX -= 0.1f;
break;
}
}
The preview can be separated from the recording with different loops and you could have a low res preview, but save more data, still, it would be slow.
I've got another suggestion: Record to the .oni format instead. If you've installed OpenNI, you could make use of a couple of samples like NiViewer and NiBackRecorder. SimpleOpenNI also exposes this functionality, have a look at the RecorderPlay sample.
I suggest trying something like this:
Record your scene to an .oni file. It should be fast/responsive
When you're happy with you're .oni recording, process each frame (convert depth to x,y,z points/ filter as needed/ save to the desired format/etc.)
Here's another sketch to illustrate the idea:
import SimpleOpenNI.*;
SimpleOpenNI context;
boolean recordFlag = true;
int frames = 0;
void setup(){
context = new SimpleOpenNI(this);
if(! recordFlag){
if(! context.openFileRecording("test.oni") ){
println("can't find recording !!!!");
exit();
}
context.enableDepth();
}else{
// recording
context.enableDepth();
// setup the recording
context.enableRecorder(SimpleOpenNI.RECORD_MEDIUM_FILE,"test.oni");
// select the recording channels
context.addNodeToRecording(SimpleOpenNI.NODE_DEPTH,SimpleOpenNI.CODEC_16Z_EMB_TABLES);
}
// set window size
if((context.nodes() & SimpleOpenNI.NODE_DEPTH) != 0)
size(context.depthWidth() , context.depthHeight());
else
exit();
}
void draw()
{
background(0);
context.update();
if((context.nodes() & SimpleOpenNI.NODE_DEPTH) != 0) image(context.depthImage(),0,0);
if(recordFlag) frames++;
}
void keyPressed(){
if(key == ' '){
if(recordFlag){
saveStrings(dataPath("frames.txt"),split(frames+" ",' '));
exit();
}else saveONIToPLY();
}
}
void saveONIToPLY(){
frames = int(loadStrings(dataPath("frames.txt"))[0]);
println("recording " + frames + " frames");
int w = context.depthWidth();
int h = context.depthHeight();
noLoop();
for(int i = 0 ; i < frames; i++){
PrintWriter output = createWriter(dataPath("frame_"+i+".ply"));
output.println("ply");
output.println("format ascii 1.0");
output.println("element vertex " + (w*h));
output.println("property float x");
output.println("property float y");
output.println("property float z");
output.println("end_header\n");
context.update();
int[] depthMap = context.depthMap();
int index;
PVector realWorldPoint;
for(int y=0;y < h;y++){
for(int x=0;x < w;x++){
index = x + y * w;
realWorldPoint = context.depthMapRealWorld()[index];
output.println(realWorldPoint.x + " " + realWorldPoint.y + " " + realWorldPoint.z);
}
}
output.flush();
output.close();
println("saved " + (i+1) + " of " + frames);
}
loop();
println("recorded " + frames + " frames");
}
When the recordFlag is set to true, data will be saved to an .oni file.
I haven't found anything in the docs to read how many frames there are in an .oni file so as a quick workaround I've added the frame counter. If you hit space, the recording will stop, but will also save the number of frames in a txt file then exit the app. This will be useful later.
When the recordFlag is set to false, if there is a recording already, it will playback.
If you hit space in this 'mode', drawing will stop, the frame number will be load from the .txt file and for each frame:
The context will be updated (moving to the next frame)
Each pixel in the depth map will be converted to a point
ALL the points will be written to a .ply file (you can process with meshlab)
After all frames were saved, the sketch will resume drawing. Since there's no 3D drawing and the sketch is fairly simple, performance should be better, but bare in mind that large .oni file will require a lot of RAM. Feel free to modify the sketch to your needs (e.g. filter out the information you don't want saved, etc.).
Also note that the above, although should save to PLY each separate frame, it saves the same. It seems the context doesn't update() when noLoop() has been called. Here's a modified hacky version that uses a 3s. delay (hopefully the .ply fille will be written to disk by then).
import SimpleOpenNI.*;
SimpleOpenNI context;
boolean recordFlag = false;
boolean saving = false;
int frames = 0;
int savedFrames = 0;
void setup(){
context = new SimpleOpenNI(this);
if(! recordFlag){
if(! context.openFileRecording("test.oni") ){
println("can't find recording !!!!");
exit();
}
context.enableDepth();
}else{
// recording
context.enableDepth();
// setup the recording
context.enableRecorder(SimpleOpenNI.RECORD_MEDIUM_FILE,"test.oni");
// select the recording channels
context.addNodeToRecording(SimpleOpenNI.NODE_DEPTH,SimpleOpenNI.CODEC_16Z_EMB_TABLES);
}
// set window size
if((context.nodes() & SimpleOpenNI.NODE_DEPTH) != 0)
size(context.depthWidth() , context.depthHeight());
else
exit();
}
void draw()
{
background(0);
context.update();
if((context.nodes() & SimpleOpenNI.NODE_DEPTH) != 0) image(context.depthImage(),0,0);
if(recordFlag) frames++;
if(saving && savedFrames < frames){
delay(3000);//hack
int i = savedFrames;
int w = context.depthWidth();
int h = context.depthHeight();
PrintWriter output = createWriter(dataPath("frame_"+i+".ply"));
output.println("ply");
output.println("format ascii 1.0");
output.println("element vertex " + (w*h));
output.println("property float x");
output.println("property float y");
output.println("property float z");
output.println("end_header\n");
rect(random(width),random(height),100,100);
int[] depthMap = context.depthMap();
int index;
PVector realWorldPoint;
for(int y=0;y < h;y++){
for(int x=0;x < w;x++){
index = x + y * w;
realWorldPoint = context.depthMapRealWorld()[index];
output.println(realWorldPoint.x + " " + realWorldPoint.y + " " + realWorldPoint.z);
}
}
output.flush();
output.close();
println("saved " + (i+1) + " of " + frames);
savedFrames++;
}
}
void keyPressed(){
if(key == ' '){
if(recordFlag){
saveStrings(dataPath("frames.txt"),split(frames+" ",' '));
exit();
}else saveONIToPLY();
}
}
void saveONIToPLY(){
frames = int(loadStrings(dataPath("frames.txt"))[0]);
saving = true;
println("recording " + frames + " frames");
}
I'm not sure frames and files sync and the depth data is saved at medium quality, but I hope my answer provides some ideas.

Related

Asteroids Game - Buggy Collision Detection

I am making an asteroid game using Processing 3.5.3. As you will see the collision detection is very buggy. When it detects collision between ship/asteroid sometimes it is greater than the asteroid size, sometimes it is smaller. Also, when the asteroids get smaller, the collision detect still seems to be calling the larger size asteroid. The collision between bullet and asteroid seems to only be a hit when the bullet is in the center of the asteroid.
Apologies for all the comments - they are required for my internal documentation.
Here is my code, it is broken up into classes.
Ship
class Ship {
PVector shipAcceleration;
PVector shipVelocity;
PVector shipPosition;
PShape shipShape;
float shipDirection;
int shipLastFire; //holds the time in millis that the last bullet was fired
int shipDelayTime;
Ship() {
shipAcceleration = new PVector();
shipVelocity = new PVector();
shipPosition = new PVector(width/2, height/2); // player starts in middle of screen
shipDirection = 0; // set to 0 to so "up" is a sense of direction
shipLastFire = 0;
shipDelayTime = 300;
keys = new boolean[5];
shipShape = createShape();
shipShape.beginShape();
shipShape.fill(255, 0, 0);
shipShape.vertex(0, -4);
shipShape.vertex(2, 0);
shipShape.vertex(2, 2);
shipShape.vertex(0, 1);
shipShape.vertex(-2, 2);
shipShape.vertex(-2, 0);
shipShape.vertex(0, -4);
shipShape.endShape();
}
void moveShip() {
shipShape.resetMatrix();
// reset.Matrix sourced from https://processing.org/reference/resetMatrix_.html
shipShape.rotate(radians(shipDirection)); // rotates ship
shape(shipShape, shipPosition.x, shipPosition.y, 10, 10);
}
void updateShip() {
// motion sourced from Chapter 22 of 'Processing: A programming handbook
// for visual designers and asrtists' by Casey Reas and Ben Fry
shipAcceleration.x = 0;
shipAcceleration.y = 0;
if (keys[0]) {
shipAcceleration.x = 0.5 * cos(radians(shipDirection) - PI/2);
shipAcceleration.y = 0.5 * sin(radians(shipDirection) - PI/2);
}
if (keys[1] && !keys[2])
{
shipDirection -= 5;
}
if (keys[2] && !keys[1])
{
shipDirection += 5;
}
shipVelocity.add(shipAcceleration);
// add sourced from https://processing.org/reference/PVector_add_.html
shipPosition.add(shipVelocity);
shipVelocity.mult(.95);
// mult sourced from https://processing.org/reference/PVector_mult_.html
shipPosition.x %= width;
if (shipPosition.x < -10)
{
shipPosition.x = width;
}
shipPosition.y %= height;
if (shipPosition.y < -10)
{
shipPosition.y = height;
}
if (keys[4]) {
if (millis() - shipLastFire > shipDelayTime) {
shipLastFire = millis();
fireBullet(shipPosition, shipVelocity, shipDirection);
}
}
}
}
Bullet
class Bullet {
PVector bulletPosition;
PVector bulletVelocity;
boolean bulletHidden; // used if lifespan is max and to help recycle
int bulletSize;
int bulletCreationTime;
int bulletLifespan; //the time in milli seconds that bullets last
int bulletSpeed;
Bullet() {
bulletHidden = true;
bulletSize = 5;
bulletPosition = new PVector();
bulletVelocity = new PVector();
bulletCreationTime = 0;
bulletLifespan = 3000;
bulletSpeed = 5;
}
void updateBullet() {
if (!bulletHidden) {
bulletPosition.add(bulletVelocity);
if (millis() - bulletCreationTime > bulletLifespan)
// millis sourced from https://processing.org/reference/millis_.html
{
bulletHidden = true;
}
bulletPosition.x %= width;
if (bulletPosition.x < -1)
{
bulletPosition.x = width;
}
bulletPosition.y %= height;
if (bulletPosition.y < -1)
{
bulletPosition.y = height;
}
}
}
void drawBullet() {
if (!bulletHidden) {
updateBullet();
ellipse(bulletPosition.x, bulletPosition.y, bulletSize, bulletSize);
}
}
void reset(PVector pos, PVector spe, float direct) {
bulletPosition = new PVector(pos.x + (20 * cos(radians(direct) - PI/2)), pos.y + (20 * sin(radians(direct) - PI/2)));
bulletVelocity.x = bulletSpeed * cos(radians(direct) - PI/2) + spe.x;
bulletVelocity.y = bulletSpeed * sin(radians(direct) - PI/2) + spe.y;
bulletCreationTime = millis();
bulletHidden = false;
}
}
Asteroid
class Asteroid {
float asteroidSize = (width/80*12);
float x;
float y;
float velocityX;
float velocityY;
PVector[] vertices = new PVector[8];
boolean active = true; //false after collision
int level = 1; // how many times has it been shot. Level 1 is not yet shot
Asteroid(float xPos, float yPos, int aLevel) {
if (xPos == 0 && yPos == 0) { //if begin level asteroids
x = random(width) ; // set random start positions
y = random (height);
} else { // if collision generating 2 smaller asteroids
x = xPos; // set from asteroid x, y
y = yPos;
}
velocityX = random(-2, 2);
velocityY = random(-2, 2);
level = aLevel; //sets asteroid level (how many times shot)
//create polygon. /aLevel generates smaller polygons with each collision.
vertices[0] = new PVector(random (width/80*3/aLevel), random(height/80*3/aLevel) );
vertices[1] = new PVector(random((width/80*4/aLevel), (width/80*8/aLevel)), random(height/80*3/aLevel) );
vertices[2] = new PVector(random((width/80*9/aLevel), (width/80*12/aLevel)), random(height/80*3/aLevel) );
vertices[3] = new PVector(random((width/80*9/aLevel), (width/80*12/aLevel)), random((height/80*4/aLevel), (height/80*8/aLevel)) );
vertices[4] = new PVector(random((width/80*9/aLevel), (width/80*12/aLevel)), random((height/80*9/aLevel), (height/80*12/aLevel)) );
vertices[5] = new PVector(random((width/80*4/aLevel), (width/80*8/aLevel)), random((height/80*9/aLevel), (height/80*12/aLevel)) );
vertices[6] = new PVector(random(width/80*3/aLevel), random((height/80*9/aLevel), (height/80*12/aLevel)) );
vertices[7] = new PVector(random(width/80*3/aLevel), random((height/80*4/aLevel), (height/80*8/aLevel)) );
}
void moveAsteroid() {
x = x + velocityX; //asteroids to move with a random velocity
y = y + velocityY;
if ( x < -1 * asteroidSize ) {
x = width + asteroidSize;
} //if off screen left, come back in right
if ( x > width + asteroidSize ) {
x = -1 * asteroidSize;
} // if off screen right, come back in left
if ( y < -1 * asteroidSize ) {
y = height + asteroidSize;
} //if off top of screen, come back in bottom
if ( y > height + asteroidSize ) {
y = -1 * asteroidSize ;
} //if off bottom of screen, come back in top
}
void asteroidDraw() {
if (active == false) { // If not active don't draw
return;
}
stroke(150);
fill(255);
// this was how I orginally coded. Have kept commented out for now, so I can see what I did, but will delete before submission.
/*beginShape();
vertex(vertices[0].x, vertices[0].y );
vertex(vertices[1].x, vertices[1].y );
vertex(vertices[2].x, vertices[2].y );
vertex(vertices[3].x, vertices[3].y );
vertex(vertices[4].x, vertices[4].y );
vertex(vertices[5].x, vertices[5].y );
vertex(vertices[6].x, vertices[6].y );
vertex(vertices[7].x, vertices[7].y );
endShape(CLOSE); */
beginShape();
for (PVector v : vertices) {
vertex(x+v.x, y+v.y);
}
endShape(CLOSE);
}
void manDown() {
active = false; //sets to in active so will stop drawing
// add 2 new asteroids to array
asteroids = (Asteroid[]) append( asteroids, new Asteroid( x+20, y+20, level + 1 ) ); // Appends asteroid to array. Changing level makes the asteroid smaller.
asteroids = (Asteroid[]) append( asteroids, new Asteroid( x-20, y-20, level + 1 ) ); // Appends two smaller asteroids to array.
}
}
Game Manager
class GameManager {
int scoreCount;
boolean gameState = true;
int lifeCount;
void newGame()
{
gameState = true; //sets game state to in play
scoreCount = 0; //set counter of flies killed to 0
lifeCount = 3;
}
void scoreUpdate()
{
textSize(width*3/100);
textAlign(LEFT);
fill(255);
text("Score " + scoreCount, (width*2/100), (height*4/100));
}
void lifeLost()
{
lifeCount = lifeCount - 1;
if (lifeCount <= 0) {
gameState = false;
gameOver();
}
}
void lifeUpdate()
{
textSize(height*3/100);
textAlign(LEFT);
fill(255);
text("Lives " + lifeCount, (width*2/100), ((height*4/100) + (height*3/100)) );
}
void gameOver()
{
background(0);
textSize(height*5/100);
textAlign(CENTER);
fill(255);
text("Game over", width/2, height/2.6);
//play again button
fill(255);
rect(((width/2)-(width/4)), (((height/2)- (height/12))), width/2, height/8);
fill(0);
text("Play Again", width/2, height/2);
//define area for play again button collision
if (mousePressed)
{
if (
(mouseX > width/4) &&
(mouseX < width/4 +width/2) &&
(mouseY > (height/2-height/10.5)) &&
(mouseY < ((height/2-height/10.5) + height/8))
)
{
setup(); //reset game
}
}
}
}
Main
Asteroid[] asteroids; //K Level 1 starts with 6, add 2 each level, 10 levels
Ship myShip;
GameManager gameManager;
ArrayList<Bullet> bullets;
// Array help sourced from chapter 28 of 'Processing: A programming handbook
// for visual designers and asrtists' by Casey Reas and Ben Fry
int bulletIndex; // used to recycle bullets
// index sourced from https://py.processing.org/reference/list_index.html
int startNum = 6; //K begin game with 6 asteroids in the level
boolean[] keys; // boolean for storing keypressed/released
void setup() {
size(800, 800);
gameManager = new GameManager();
gameManager.newGame();
bulletIndex = 0;
bullets = new ArrayList<Bullet>();
keys = new boolean[5];
myShip = new Ship();
asteroids = new Asteroid [startNum]; //K
for (int a = 0; a < startNum; a++) { //K create asteroids in array
asteroids[a] = new Asteroid(0, 0, 1); //K
}
for (int i = 0; i < 20; i++)
{
bullets.add(new Bullet()); // create bullets
}
}
void draw() {
background(0);
collisionDetect();
gameManager.gameState = true;
myShip.updateShip(); // E
myShip.moveShip(); // E
for (int a = 0; a < asteroids.length; a++) { //K for asteroids in array
asteroids[a].moveAsteroid(); //K
asteroids[a].asteroidDraw(); //K
}
gameManager.scoreUpdate();
gameManager.lifeUpdate();
for (int i = 0; i < bullets.size(); i++)
{
bullets.get(i).drawBullet(); // drawing bullets
}
}
void keyPressed() {
if (key == CODED) {
if (keyCode == UP)
keys[0] = true;
if (keyCode == LEFT)
keys[1] = true;
if (keyCode == RIGHT)
keys[2] = true;
if (keyCode == DOWN)
keys[3] = true;
} else {
if (key == 'w')
keys[0] = true;
if (key == 'a')
keys[1] = true;
if (key == 'd')
keys[2] = true;
if (key == 's')
keys[3] = true;
if (key == ' ')
keys[4] = true;
}
}
void keyReleased() {
if (key == CODED) {
if (keyCode == UP)
keys[0] = false;
if (keyCode == LEFT)
keys[1] = false;
if (keyCode == RIGHT)
keys[2] = false;
if (keyCode == DOWN)
keys[3] = false;
} else {
if (key == 'w')
keys[0] = false;
if (key == 'a')
keys[1] = false;
if (key == 'd')
keys[2] = false;
if (key == 's')
keys[3] = false;
if (key == ' ')
keys[4] = false;
}
}
void fireBullet(PVector pos, PVector spe, float dir) {
bullets.get(bulletIndex).reset(pos, spe, dir);
// set attributes of last used bullet
// get sourced from https://processing.org/reference/get_.html
bulletIndex++; //update index
bulletIndex %= bullets.size(); //keep index in range
}
void collisionDetect(){
Asteroid testHolder;
Bullet bulletHolder;
// asteroid and bullet objects to minimize creating new objects
for(int i = 0; i < asteroids.length; i++){
testHolder = asteroids[i];
if(dist(testHolder.x, testHolder.y, myShip.shipPosition.x,
myShip.shipPosition.y) < testHolder.asteroidSize)
// collision of player and the asteroid
{gameManager.gameOver();}
for(int j = 0; j < bullets.size(); j++){
bulletHolder = bullets.get(j);
// pull and store each bullet from the list
if(bulletHolder.bulletHidden){continue;}
// don't calculate anything if it is hidden
if(dist(testHolder.x, testHolder.y, bulletHolder.bulletPosition.x,
bulletHolder.bulletPosition.y) < testHolder.asteroidSize){
testHolder.manDown();
// used to detect collision and split if collided
bulletHolder.bulletHidden = true;
// hide the bullet so it won't go 'through' the asteroids
j++;
}
}
}
}
For the problem with the smaller asteroids, you need to make the asteroidSize dependent on the level. Currently they are all the same: float asteroidSize = (width/80*12);
As the to collision issue, the first thing is that you also have to take the size to the ship/bullet hitting the asteroid into account:
if(dist(testHolder.x, testHolder.y, myShip.shipPosition.x, myShip.shipPosition.y) < (testHolder.asteroidSize + myShip.size))
For clarity: size is in both cases the radius.
Second, there will always be some area's where this basic type of collision detection does not follow the visual, because your shapes are not circles. The randomness that you use for the asteroids does not help in that respect. A way to get more control over this is to define a couple of shapes per level, and pick one of those at random when creating an asteroid. This way you can tweak the shape/radius to make a good trade off between looks and function so it looks 'believable enough'.

Automation of selection in Processing

I am currently using a processing sketch to work through a large number of images I have in a folder. I have set an onClick command to advance to the next image in the string. It is quite time consuming and I would like to automate the action that once the sketch is completed the sketch would repeat it's self selecting the next image from the string. The onClick command also save the image the export folder but each time saves with the same file name, I've tried to set up sequential numbering but it hasn't worked so far. Any help would be greatly appreciated.
String[] imgNames = {"1.jpg", "2.jpg", "3.jpg"};
PImage img;
int imgIndex = 0;
void nextImage() {
background(255);
frameRate(10000);
loop();
frameCount = 0;
img = loadImage(imgNames[imgIndex]);
img.loadPixels();
imgIndex += 1;
if (imgIndex >= imgNames.length) {
imgIndex = 0;
}
}
void paintStroke(float strokeLength, color strokeColor, int strokeThickness) {
float stepLength = strokeLength/4.0;
// Determines if the stroke is curved. A straight line is 0.
float tangent1 = 0;
float tangent2 = 0;
float odds = random(1.0);
if (odds < 0.7) {
tangent1 = random(-strokeLength, strokeLength);
tangent2 = random(-strokeLength, strokeLength);
}
// Draw a big stroke
noFill();
stroke(strokeColor);
strokeWeight(strokeThickness);
curve(tangent1, -stepLength*2, 0, -stepLength, 0, stepLength, tangent2, stepLength*2);
int z = 1;
// Draw stroke's details
for (int num = strokeThickness; num > 0; num --) {
float offset = random(-50, 25);
color newColor = color(red(strokeColor)+offset, green(strokeColor)+offset, blue(strokeColor)+offset, random(100, 255));
stroke(newColor);
strokeWeight((int)random(0, 3));
curve(tangent1, -stepLength*2, z-strokeThickness/2, -stepLength*random(0.9, 1.1), z-strokeThickness/2, stepLength*random(0.9, 1.1), tangent2, stepLength*2);
z += 1;
}
}
void setup() {
size(1600, 700);
nextImage();
}
void draw() {
translate(width/2, height/2);
int index = 0;
for (int y = 0; y < img.height; y+=1) {
for (int x = 0; x < img.width; x+=1) {
int odds = (int)random(20000);
if (odds < 1) {
color pixelColor = img.pixels[index];
pixelColor = color(red(pixelColor), green(pixelColor), blue(pixelColor), 100);
pushMatrix();
translate(x-img.width/2, y-img.height/2);
rotate(radians(random(-90, 90)));
// Paint by layers from rough strokes to finer details
if (frameCount < 20) {
// Big rough strokes
paintStroke(random(150, 250), pixelColor, (int)random(20, 40));
} else if (frameCount < 1000) {
// Thick strokes
paintStroke(random(75, 125), pixelColor, (int)random(8, 12));
} else if (frameCount < 1500) {
// Small strokes
paintStroke(random(20, 30), pixelColor, (int)random(1, 4));
} else if (frameCount < 10000) {
// Big dots
paintStroke(random(5, 10), pixelColor, (int)random(5, 8));
} else if (frameCount < 10000) {
// Small dots
paintStroke(random(1, 2), pixelColor, (int)random(1, 3));
}
popMatrix();
}
index += 1;
}
}
if (frameCount > 10000) {
noLoop();
}
// if(key == 's'){
// println("Saving...");
// saveFrame("screen-####.jpg");
// println("Done saving.");
// }
}
void mousePressed() {
save("001.tif");
nextImage();
}
Can't you just call the nextImage() function from inside this if statement?
if (frameCount > 10000) {
noLoop();
}
Would be:
if (frameCount > 10000) {
nextImage();
}
As for using different filenames, just use an int that you increment whenever you save the file, and use that value in the save() function. Or you could use the frameCount variable:
save("img" + frameCount + ".tif");
If you have follow-up questions, please post a MCVE instead of your whole project. Try to isolate one problem at a time in a small example program that only does that one thing. Good luck.

Processing - deprecated OpenKinect library

I am trying to replicate a project for Kinect for this music video, but the code is seriously outdated.
After weeks searching, I have not found anything about this.
I would be greatly thankful to anyone who points out to me what is deprecated in the following code:
(I'm using Processing 3)
import org.openkinect.*;
import org.openkinect.processing.*;
import java.io.*;
// Kinect Library object
Kinect kinect;
float a = 0;
// Size of kinect image
int w = 640;
int h = 480;
// writing state indicator
boolean write = false;
// treshold filter initial value
int fltValue = 950;
// "recording" object. each vector element holds a coordinate map vector
Vector <Object> recording = new Vector<Object>();
// We'll use a lookup table so that we don't have to repeat the math over and over
float[] depthLookUp = new float[2048];
void setup() {
size(800,600,P3D);
kinect = new Kinect(this);
kinect.start();
kinect.enableDepth(true);
// We don't need the grayscale image in this example
// so this makes it more efficient
kinect.processDepthImage(false);
// Lookup table for all possible depth values (0 - 2047)
for (int i = 0; i < depthLookUp.length; i++) {
depthLookUp[i] = rawDepthToMeters(i);
}
}
void draw() {
background(0);
fill(255);
textMode(SCREEN);
text("Kinect FR: " + (int)kinect.getDepthFPS() + "\nProcessing FR: " + (int)frameRate,10,16);
// Get the raw depth as array of integers
int[] depth = kinect.getRawDepth();
// We're just going to calculate and draw every 4th pixel (equivalent of 160x120)
int skip = 4;
// Translate and rotate
translate(width/2,height/2,-50);
rotateY(a);
//noStroke();
//lights();
int index = 0;
PVector[] frame = new PVector[19200];
for(int x=0; x<w; x+=skip) {
for(int y=0; y<h; y+=skip) {
int offset = x+y*w;
// Convert kinect data to world xyz coordinate
int rawDepth = depth[offset];
boolean flt = true;
PVector v = depthToWorld(x,y,rawDepth);
if (flt && rawDepth > fltValue)
{
v = depthToWorld(x,y,2047);
}
frame[index] = v;
index++;
stroke(map(rawDepth,0,2048,0,256));
pushMatrix();
// Scale up by 200
float factor = 400;
translate(v.x*factor,v.y*factor,factor-v.z*factor);
//sphere(1);
point(0,0);
//line (0,0,1,1);
popMatrix();
}
}
if (write == true) {
recording.add(frame);
}
// Rotate
//a += 0.015f;
}
// These functions come from:http://graphics.stanford.edu/~mdfisher/Kinect.html
float rawDepthToMeters(int depthValue) {
if (depthValue < 2047) {
return (float)(1.0 / ((double)(depthValue) * -0.0030711016 + 3.3309495161));
}
return 0.0f;
}
PVector depthToWorld(int x, int y, int depthValue) {
final double fx_d = 1.0 / 5.9421434211923247e+02;
final double fy_d = 1.0 / 5.9104053696870778e+02;
final double cx_d = 3.3930780975300314e+02;
final double cy_d = 2.4273913761751615e+02;
PVector result = new PVector();
double depth = depthLookUp[depthValue];//rawDepthToMeters(depthValue);
result.x = (float)((x - cx_d) * depth * fx_d);
result.y = (float)((y - cy_d) * depth * fy_d);
result.z = (float)(depth);
return result;
}
void stop() {
kinect.quit();
super.stop();
}
int currentFile = 0;
void saveFile() {
}
void keyPressed() { // Press a key to save the data
if (key == '1')
{
fltValue += 50;
println("fltValue: " + fltValue);
}
else if (key == '2')
{
fltValue -= 50;
println("fltValue: " + fltValue);
}
else if (key=='4'){
if (write == true) {
write = false;
println( "recorded " + recording.size() + " frames.");
// saveFile();
// save
Enumeration e = recording.elements();
println("Stopped Recording " + currentFile);
int i = 0;
while (e.hasMoreElements()) {
// Create one directory
boolean success = (new File("out"+currentFile)).mkdir();
PrintWriter output = createWriter("out"+currentFile+"/frame" + i++ +".txt");
PVector [] frame = (PVector []) e.nextElement();
for (int j = 0; j < frame.length; j++) {
output.println(j + ", " + frame[j].x + ", " + frame[j].y + ", " + frame[j].z );
}
output.flush(); // Write the remaining data
output.close();
}
currentFile++;
}
}
else if (key == '3') {
println("Started Recording "+currentFile);
recording.clear();
write = true;
}
}
If the code works, then I wouldn't worry too much about it. Deprecated can just mean that a newer version is available, not that the older version stopped working.
However, if the code does not work, then updating to a newer library is probably a good idea anyway. Check out the library section of the Processing homepage, which lists several Kinect libraries.
In fact, one of those libraries is the updated version of the old library you're using: Open Kinect for Processing.
Edit: It looks like both of the errors you mentioned are due to missing import statements. You need to import both Vector and Enumeration to use them:
import java.util.Vector;
import java.util.Enumeration;

Save image with Processing

I'm trying to save an image after certain time, the problem is that the image size is bigger than the display so when I use the save or saveFrame function it only saves the image that I can see in the display. There is any other way to save the whole image?
This is my code:
PImage picture, pictureFilter, img;
int total, cont, current;
ArrayList<ArrayList<Position>> columns;
String[] fontList;
public class Position {
public int x;
public int y;
}
void setup() {
fontList = PFont.list();
picture = loadImage("DSC05920b.JPG");
pictureFilter = loadImage("filtrePort2.jpg");
frame.setResizable(true);
size(picture.width, picture.height);
columns = new ArrayList<ArrayList<Position>>();
for(int i = 0; i < picture.width; i++) {
ArrayList<Position> row = new ArrayList<Position>();
for(int j = 0; j < picture.height; j++){
Position p = new Position();
p.x = i;
p.y = j;
row.add(p);
}
columns.add(row);
}
total = picture.width * picture.height;
cont = total;
current = 0;
img = createImage(picture.width, picture.height, RGB);
}
float randomLetter() {
float value = 23;
boolean found = false;
while(!found) {
value = random(48, 122);
if(value >48 && value <58) found = true;
if(value >65 && value <91) found = true;
if(value >97 && value <123) found = true;
}
return value;
}
void draw() {
int x = int(random(0, columns.size()));
ArrayList<Position> rows = columns.get(x);
int y = int(random(0, rows.size()));
Position p = rows.get(y);
color c = pictureFilter.get(p.x, p.y);
int r = (c >> 16) & 0xFF; // Faster way of getting red(argb)
if(r < 240) {
PFont f = createFont(fontList[int(random(0,fontList.length))],random(5, 24),true);
textFont(f);
fill(picture.get(p.x,p.y));
char letter = (char) int(randomLetter());
text(letter, p.x, p.y);
}
if(rows.size() == 1) {
if(columns.size() == 1) {
saveFrame("lol.jpg");
columns.remove(x);
} else {
columns.remove(x);
}
} else {
println(rows.size());
rows.remove(y);
}
--cont;
float percent = float(total-cont)/float(total)*100;
if(int(percent) != current) {
current = int(percent);
save("image_" + current + ".jpg");
}
println("DONE: " + (total-cont) + "/" + total + " Progress: " + percent + "%");
}
The code do a lot of stuff but the part that its not working well is at the final when I check if the percentage have been increased in order to save the image
You can write this into a PGraphics context - aka a graphics buffer.
The buffer can be as big as you need it to be, and you can choose whether to draw it on the screen or not..
// Create the buffer at the size you need, and choose the renderer
PGraphics pg = createGraphics(myImage.width, myImage.height, P2D);
// Wrap all your drawing functions in the pg context - e.g.
PFont f = createFont(fontList[int(random(0,fontList.length))],random(5, 24),true);
textFont(f);
pg.beginDraw();
pg.fill(picture.get(p.x,p.y));
char letter = (char) int(randomLetter());
pg.text(letter, p.x, p.y);
pg.endDraw();
// Draw your PG to the screen and resize the representation of it to the screen bounds
image(pg, 0, 0, width, height); // <-- this wont actually clip/resize the image
// Save it
pg.save("image_" + current + ".jpg");
The PImage class contains a save() function that exports to file. The API should be your first stop for questions like this.

How to efficiently store past depth pixel data from depthMapRealWorld() method?

I can't get around a peculiar problem with SimpleOpenNI for Processing ao I'm asking for your help.
I'd like to store snapshots of pixel depth data (returned by .depthMapRealWorld() method as PVector arrays) on discrete time intervals, then process them further for a presentation. I tried adding them in an ArrayList, but it seems that the depthMapRealWorld() method is returning only a reference to a current depth data, not a real array. I tried in this sequence:
Just getting the data and adding it in an arraylist. On every call of the update() method the whole arraylist contained the same PVector array, even if the array at the zero position was added many iterations away!
Then I made the PVector array, along with its creation time, part of a class. Rewrote the sketch a little, but it didn't help. All of the arrays in the arraylist werw still the same.
Finally, in the constructor of the class, I "manually" copied the xyz coordinates of every vector from the PVector array into a int array. That seemed to solve the problem - the int arrays in the arraylist are now different from each other. But this solution introduced serious performance problems.
The question is: is there a more efficient way of storing these PVector arrays and retaining their value?
code:
import processing.opengl.*;
import SimpleOpenNI.*;
SimpleOpenNI kinect;
float rotation = 0;
int time = 0;
ArrayList dissolver;
ArrayList<Integer> timer;
int pSize = 10;
Past past;
void setup() {
dissolver = new ArrayList();
timer = new ArrayList();
size(1024, 768, OPENGL);
kinect = new SimpleOpenNI(this);
kinect.enableDepth();
translate(width/2, height/2, -100);
rotateX(radians(180));
stroke(255);
}
void draw() {
background(0);
translate(width/2, height/2, 500);
rotateX(radians(180));
kinect.update();
stroke (255, 255, 255);
past = new Past (kinect.depthMapRealWorld(), time);
if (dissolver.size() == pSize) { //remove the oldest arraylist element if when list gets full
dissolver.remove(0); //
}
if (time % 20 == 0) {
dissolver.add (past);
Past p1 = (Past) dissolver.get (0);
float [][] o2 = p1.getVector();
println ("x coord of a random point at arraylist position 0: " + o2[50000][0]); //for testing
}
if (dissolver.size() == pSize-1) {
//dissolve ();
}
time ++;
}
void dissolve () { //from the previous nonworking version; ignore
for (int offset = 0; offset < pSize-1; offset ++) {
PVector[] offPoints = (PVector[]) dissolver.get (offset);
int offTime = timer.get(offset);
for (int i = 0; i < offPoints.length; i+=10) {
int col = (time-offTime)*2; //why??
stroke (255, 0, col);
PVector currentPoint = offPoints[i];
if (currentPoint.z <1500) {
point(currentPoint.x, currentPoint.y, currentPoint.z); // - 2*(time-offTime) + random(0, 100)
}
}
}
}
class Past {
private PVector [] depth; //should contain this, not int
private float [][] depth1;
private int time;
Past (PVector [] now, int t) {
//should be like this: depth = now;
//clumsy and performancewise catastrophic solution below
depth1 = new float [now.length][3];
for (int i = 0; i< now.length; i+=10) {
PVector temp = now[i];
depth1 [i][0] = temp.x;
depth1 [i][1] = temp.y;
depth1 [i][2] = temp.z;
}
//arrayCopy(now, depth); this didn't work either
time = t;
}
float [][] getVector () {
return depth1;
}
int getTime () {
return time;
}
}
If I understood correctly, you want to store the 3D positions(ArrayList of PVectors) for each frame, right ?
If so, you should be able to simply store PVectors and reference them later.
Here's a basic sketch to illustrate this:
import processing.opengl.*;
import SimpleOpenNI.*;
SimpleOpenNI kinect;
ArrayList<ArrayList<PVector>> frames = new ArrayList<ArrayList<PVector>>();
ArrayList<PVector> frame;
boolean isRecording = true;
boolean isRecFrame;
void setup() {
size(1024, 768, OPENGL);
kinect = new SimpleOpenNI(this);
kinect.enableDepth();
stroke(255);
}
void draw() {
background(0);
translate(width/2, height/2, 500);
rotateX(PI);
translate(0,0,-1000);
kinect.update();
if(isRecording){
isRecFrame = (frameCount % 20 == 0);//record every 20 frames
int[] depthMap = kinect.depthMap();
int steps = 5; // to speed up the drawing, draw every N point
int index;
PVector realWorldPoint;
if(isRecFrame) frame = new ArrayList<PVector>();
for(int y=0;y < kinect.depthHeight();y+=steps)
{
for(int x=0;x < kinect.depthWidth();x+=steps)
{
index = x + y * kinect.depthWidth();
if(depthMap[index] > 0)
{
realWorldPoint = kinect.depthMapRealWorld()[index];
point(realWorldPoint.x,realWorldPoint.y,realWorldPoint.z);
if(isRecFrame) frame.add(realWorldPoint.get());
}
}
}
if(isRecFrame) frames.add(frame);
}else{//playback
ArrayList<PVector> currentFrame = frames.get(frameCount%frames.size());//playback is faster than recording now for testing purposes - add a decent frame counter here at some point
for(PVector p : currentFrame) point(p.x,p.y,p.z);
}
}
void keyPressed(){
if(key == ' ') isRecording = !isRecording;
}
Use the SPACE key to toggle between recording and playback.
The main thing to note is I'm storing a copy of the real world position for each depth pixel (frame.add(realWorldPoint.get());). Another thing to keep in mind is that currently you're storing these coordinates in memory which at some point will fill. If you only store a limited number of frames that should be fine, if not you might want to save to the points to disk. This way you can reuse recordings with other sketches. A basic way would be to sore them in a csv file:
void saveCSV(ArrayList<PVector> pts){
String csv = "x,y,z\n";
for(PVector p : pts) csv += p.x + "," + p.y + "," + p.z + "\n";
saveStrings("frame_"+frameCount+".csv",csv.split("\n"));
}
Another would be to use a more suitable format for point clouds, like PLY.
Saving an ASCII PLY is fairly straight forward:
void savePLY(ArrayList<PVector> pts){
String ply = "ply\n";
ply += "format ascii 1.0\n";
ply += "element vertex " + pts.size() + "\n";
ply += "property float x\n";
ply += "property float y\n";
ply += "property float z\n";
ply += "end_header\n";
for(PVector p : pts)ply += p.x + " " + p.y + " " + p.z + "\n";
saveStrings("frame_"+frameCount+".ply",ply.split("\n"));
}
You can later open/explore/process these files with tools like MeshLab.

Resources