Processing で Kinect や Xtion が使える Simple OpaneNI を試してみました


Procesing から Kinect や Xtion を使えるようにする SImple OpenNI(http://code.google.com/p/simple-openni/)を試してみました。

今回は以前勉強会用に作ったこれ(http://www.digifie.jp/blog/archives/579)を Simple OpenNI 版に改造してみました。

OpenNI 版では…

・Xtion と Flash
  Xtion(OpenNI)→ AIR2.0(NativeProcess)を使用して…
  Xtion で取得した手の座標を送信する簡易サーバをつくり…
  RTMFP で Flash に値を送信しています。

という手順で Flash に Xtion から取得した座標情報を送っていたのですが、
今回の Simple OpenNI 版では…

・Xtion と Flash
  Xtion(Simple OpenNI)→ TCPソケットを作って…
  ソケット経由 で Flash に値を送信しています。

簡単ですね。
というわけで、こんな感じで動くようになりました。
以前の OpenNI 版と同様 RTMFP 経由で iPhone も使用していますが、この部分については これ と同じです。
 

 
そして今回使用した Processing のコードです。

import SimpleOpenNI.*;
import processing.net.*;


SimpleOpenNI      context;

// NITE
XnVSessionManager sessionManager;
XnVFlowRouter     flowRouter;

PointDrawer       pointDrawer;

//
Server server ;
Client client ;
Boolean isPointTracking;

void setup()
{
  context = new SimpleOpenNI(this);
   
  // mirror is by default enabled
  context.setMirror(true);
  
  // enable depthMap generation 
  if(context.enableDepth() == false)
  {
     println("Can't open the depthMap, maybe the camera is not connected!"); 
     exit();
     return;
  }
  
  // enable the hands + gesture
  context.enableGesture();
  context.enableHands();
 
  // setup NITE 
  sessionManager = context.createSessionManager("Click,Wave", "RaiseHand");

  pointDrawer = new PointDrawer();
  flowRouter = new XnVFlowRouter();
  flowRouter.SetActive(pointDrawer);
  
  sessionManager.AddListener(flowRouter);
           
  size(context.depthWidth(), context.depthHeight()); 
  smooth();
  
  server = new Server(this, 5204) ;
}

void draw()
{
  background(200,0,0);
  // update the cam
  context.update();
  
  // update nite
  context.update(sessionManager);
  
  // draw depthImageMap
  image(context.depthImage(),0,0);
  
  // draw the list
  pointDrawer.draw();
}

void keyPressed()
{
  switch(key)
  {
  case 'e':
    // end sessions
    sessionManager.EndSession();
    println("end session");
    break;
  }
}

/////////////////////////////////////////////////////////////////////////////////////////////////////
// session callbacks

void onStartSession(PVector pos)
{
  println("onStartSession: " + pos);
}

void onEndSession()
{
  println("onEndSession: ");
}

void onFocusSession(String strFocus,PVector pos,float progress)
{
  println("onFocusSession: focus=" + strFocus + ",pos=" + pos + ",progress=" + progress);
}


/////////////////////////////////////////////////////////////////////////////////////////////////////
// PointDrawer keeps track of the handpoints

class PointDrawer extends XnVPointControl
{
  HashMap    _pointLists;
  int        _maxPoints;
  color[]    _colorList = { color(255,0,0),color(0,255,0),color(0,0,255),color(255,255,0)};
  
  
  public PointDrawer()
  {
    _maxPoints = 30;
    _pointLists = new HashMap();
  }
	
  public void OnPointCreate(XnVHandPointContext cxt)
  {
    // create a new list
    addPoint(cxt.getNID(),new PVector(cxt.getPtPosition().getX(),cxt.getPtPosition().getY(),cxt.getPtPosition().getZ()));
    
    println("OnPointCreate, handId: " + cxt.getNID());
  }
  
  public void OnPointUpdate(XnVHandPointContext cxt)
  {
    //println("OnPointUpdate " + cxt.getPtPosition());   
    addPoint(cxt.getNID(),new PVector(cxt.getPtPosition().getX(),cxt.getPtPosition().getY(),cxt.getPtPosition().getZ()));
  }
  
  public void OnPointDestroy(long nID)
  {
    println("OnPointDestroy, handId: " + nID);
    
    // remove list
    if(_pointLists.containsKey(nID))
       _pointLists.remove(nID);
  }
  
  public ArrayList getPointList(long handId)
  {
    ArrayList curList;
    if(_pointLists.containsKey(handId))
      curList = (ArrayList)_pointLists.get(handId);
    else
    {
      curList = new ArrayList(_maxPoints);
      _pointLists.put(handId,curList);
    }
    return curList;  
  }
  
  public void addPoint(long handId,PVector handPoint)
  {
    ArrayList curList = getPointList(handId);
    
    curList.add(0,handPoint);      
    if(curList.size() > _maxPoints)
      curList.remove(curList.size() - 1);
  }
  
  public void draw()
  {
    if(_pointLists.size() <= 0)
    {
      isPointTracking = false;
      return;
    }
    else
    {
      isPointTracking = true;
    }
      
    pushStyle();
      noFill();
      
      PVector vec;
      PVector firstVec;
      PVector screenPos = new PVector();
      int colorIndex=0;
      
      // draw the hand lists
      Iterator<Map.Entry> itrList = _pointLists.entrySet().iterator();
      while(itrList.hasNext()) 
      {
        strokeWeight(2);
        stroke(_colorList[colorIndex % (_colorList.length - 1)]);

        ArrayList curList = (ArrayList)itrList.next().getValue();     
        
        // draw line
        firstVec = null;
        Iterator<PVector> itr = curList.iterator();
        beginShape();
          while (itr.hasNext()) 
          {
            vec = itr.next();
            if(firstVec == null)
              firstVec = vec;
            // calc the screen pos
            context.convertRealWorldToProjective(vec,screenPos);
            vertex(screenPos.x,screenPos.y); 
            //println("X:" + screenPos.x + " Y:" + screenPos.y);   
          } 
        endShape();   
  
        // draw current pos of the hand
        if(firstVec != null)
        {
          strokeWeight(8);
          context.convertRealWorldToProjective(firstVec,screenPos);
          point(screenPos.x, screenPos.y);
        }
        colorIndex++;
      }
      
    popStyle();
    
    updateSocket(screenPos.x,screenPos.y); 
  }
  
  // ----- Socket Sender ------ 
  void updateSocket(float px, float py) 
  {
    if( !isPointTracking ) return;
    String str = int(px) + "," + int(py);
    server.write(str) ;
    println(str);
  }
  
  
  // ----- Socket Receiver ------
  void receivedSocket()
  {
    if (client != null) 
    {
      String data = client.readString();
      
      if (data != null) 
      {
        println(data);
        //rcvString = data;
      }
    }
  }
  
  void serverEvent(Server srv, Client clt) 
  {
    println("connected") ;
    if (client != null) server.disconnect(client) ;
    client = clt ;
  }

}

Simple OpenNI に付いていた Hands.pde にソケット送信部分を追加しています。

Flash 側のソケット受信部分はこんな感じ。

private var _sock:Socket;
private var magniX:Number = 1024 / 640;
private var magniY:Number = 768 / 480;
private var _px:int;
private var _py:int;
		
// setup
private function socketSetup():void
{
	_sock = new Socket();
	_sock.addEventListener( IOErrorEvent.IO_ERROR, ioError );
	_sock.addEventListener( ProgressEvent.SOCKET_DATA, receive_data );
	_sock.connect( "localhost", 5204 );
	
	function ioError( e:IOErrorEvent ):void
	{
		trace( e );
	}
	
}
		
// receiver
private function receive_data(e:ProgressEvent):void
{
	var str:String = _sock.readMultiByte( _sock.bytesAvailable, "utf-8" );
	var list:Array = str.split( "," );
	
	if(list[0] >= 0 && list[1] >= 0) 
	{
		_px = list[0] * magniX;
		_py = list[1] * magniY;
	}
}

同じような方法でスケルトントラキングで取得した関節の情報も比較的簡単に取得できるので、スケルトンの情報を使用してコンテンツ的に成立するようなネタが出来たら(今はネタがありませんので…)こちらも試してみようと思っています。