1

点群データの各フレームを kinect からテキスト ファイルに保存する処理スケッチを作成しました。ファイルの各行は、kinect が登録したポイント (または頂点) です。データを 3D プログラムに取り込み、アニメーションを 3D 空間で視覚化し、さまざまな効果を適用する予定です。問題は、これを行うと、最初のフレームが適切に見え、残りのフレームが最初の画像のように見えるものを吐き出しているように見えることと、ランダムなノイズがたくさんあることです。これは私のコード全体です。適切に機能するには、単純な openni が必要です。コメントが見れます

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;
  }
}

ループは問題が発生し始める場所だと思います: for(int y=0;y < context.depthHeight();y+=steps) { など。ただし、3d 用に作成した python スクリプトの問題である可能性があります。プログラム。とにかく、これはクールなスケッチであり、3D 効果をポイント クラウド データに適用したい (またはモデルを構築したいなど) 人にとっては非常に役立つと思いますが、現時点では立ち往生しています。ご協力いただきありがとうございます!

4

1 に答える 1

7

残念ながら、今は多くを説明することはできませんが、数か月前にPLYと 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"));
}

特定の Z しきい値内のポイントのみを保存するために if ステートメントを使用していますが、必要に応じて自由に変更/使用してください。後処理のアイデアは、カタリナの Moullinex ビデオを思い起こさせます。十分に文書化されており、ソースコードも含まれています。

カタリナ 1

カタリナ 2

カタリナ 3

カタリナ 4

更新 投稿されたコードは、フレームごとに 1 つのファイルを保存します。再生速度は遅くなりますが、スケッチはフレームごとにファイルを保存する必要があります。コードを少し単純化します。

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;
  }
}

プレビューは、さまざまなループを使用して記録から分離できます。低解像度のプレビューを使用できますが、より多くのデータを保存できますが、それでも遅くなります。

別の提案があります。代わりに.oni 形式で録音してください。OpenNI をインストールしている場合は、NiViewerNiBackRecorderなどのサンプルをいくつか利用できます。SimpleOpenNI もこの機能を公開しています。RecorderPlayサンプルを見てください。

次のようなことを試すことをお勧めします。

  1. シーンを .oni ファイルに記録します。高速で応答性が高い必要があります
  2. .oni の記録に満足したら、各フレームを処理します (深度を x、y、z ポイントに変換する/必要に応じてフィルターをかける/目的の形式に保存するなど)。

アイデアを説明する別のスケッチを次に示します。

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");
}

recordFlagtrue に設定されている場合、データは .oni ファイルに保存されます。.oni ファイルに含まれるフレーム数を読み取るためのドキュメントが見つからないため、簡単な回避策としてframeカウンターを追加しました。スペースを押すと、記録は停止しますが、フレーム数を txt ファイルに保存してアプリを終了します。これは後で役に立ちます。

recordFlagが false に設定されている場合、既に録音がある場合は再生されます。この「モード」でスペースを押すと、描画が停止し、フレーム番号が .txt ファイルから読み込まれ、フレームごとに次のようになります。

  1. コンテキストが更新されます (次のフレームに移動)
  2. 深度マップの各ピクセルがポイントに変換されます
  3. すべてのポイントが .ply ファイルに書き込まれます ( meshlabで処理できます)

すべてのフレームが保存された後、スケッチは描画を再開します。3D 描画がなく、スケッチがかなり単純であるため、パフォーマンスは向上するはずですが、大きな .oni ファイルには多くの RAM が必要になることに注意してください。必要に応じてスケッチを自由に変更してください (たとえば、保存したくない情報を除外するなど)。

また、上記は PLY に個別のフレームごとに保存する必要がありますが、同じものを保存することに注意してください。noLoop() が呼び出されたときにコンテキストが update() されないようです。これは、3s を使用する変更されたハッキー バージョンです。遅延します (それまでに .ply ファイルがディスクに書き込まれることを願っています)。

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");
}

フレームとファイルが同期し、深度データが中程度の品質で保存されるかどうかはわかりませんが、私の答えがいくつかのアイデアを提供してくれることを願っています.

于 2012-07-20T16:19:24.077 に答える