KinectとProcessingで光学迷彩ごっこをしてみた

光学迷彩動画

 以前勢いで買ったKinectを色々弄っています。最初はどうすればよいのか途方に暮れていたのですが、色々な先人の方の資産を活用してなんとか動かしています。とりあえず最初に宣言した光学迷彩っぽい動画(攻殻機動隊のアレね)を作ってみました。

 完全にそれっぽい効果を狙っているだけで、背景が止まっている条件じゃないと使えませんが、少しはそれっぽいものが出来たんじゃないかと思います。もうちょっとがんばればもっとそれっぽくなると思いますが、こいつはこんなところで。
 多少要領はわかったので、次はもうちょっと面白いものつくれると思います。

ソースコードと使い方

 今回はコードいつも以上に汚いので迷いましたが公開します。起動させてしばらく待って下さい。するとカメラ画出ます。そしたらカメラの中にフレームインして下さい。そこは攻殻機動隊の世界です。スペースキーを押すと動画が生成されて終了します。動画を扱うので、Processingの他にOpenCVのライブラリのダウンロードを忘れないようにして下さい。
 カメラ画が暗い場合は、int shotWait, int delaytimeの値を大きくしてカメラ調整のための待ち時間を長くとって下さい。カメラが起動まで長過ぎて待てない人は、int shotWait, int delaytimeを逆に小さくして下さい。ただ、あんまり待ち時間を短くするとカメラが露出調整できないので注意して設定下さい。

import SimpleOpenNI.*;
SimpleOpenNI  context;

import hypermedia.video.*;
import processing.video.*;

int fps = 30;

OpenCV opencv;
MovieMaker mm;

color cBlack = color(0, 0, 0);
color cWhite = color(255, 255, 255);

PImage firstRGB1;
PImage firstRGB2;

boolean firstShot=true;

// loop count
int shot = 0;
// wait for camera boot
int shotWait = 100;
int delaytime = 20;

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

  if(context.enableDepth() == false)
  {
     println("Can't open the depthMap, maybe the camera is not connected!"); 
     exit();
     return;
  }

  context.enableScene();

  if(context.enableRGB() == false)
  {
     println("Can't open the rgbMap, maybe the camera is not connected or there is no rgbSensor!"); 
     exit();
     return;
  }

  // matching depth map with rgb map
  context.alternativeViewPointDepthToImage();

  size(context.depthWidth() , context.depthHeight());
  firstRGB1 = createGraphics(context.depthWidth(), context.depthHeight(), P2D);
  firstRGB2 = createGraphics(context.depthWidth(), context.depthHeight(), P2D);

  mm = new MovieMaker(this, context.depthWidth(), context.depthHeight(), "OpticalCamouflage.mov", fps, MovieMaker.VIDEO, MovieMaker.LOSSLESS); 
  frameRate(fps);
}

void draw()
{
  context.update(); 
  delay(delaytime);

  if(firstShot == true){
    background(0, 0, 0);
    if(shot > shotWait){
      image(context.rgbImage(), 0 , 0);
      save("screenshot.jpg");
      firstShot = false;
    }else{
      shot++;
    }
  }else{
    PImage sceneImg = context.sceneImage();
    sceneImg.loadPixels();

    PImage maskImg1 = createGraphics(context.depthWidth(), context.depthHeight(), P2D);
    PImage maskImg2 = createGraphics(context.depthWidth(), context.depthHeight(), P2D);

    PImage maskedImg1 = createGraphics(context.depthWidth(), context.depthHeight(), P2D);
    PImage maskedImg2 = createGraphics(context.depthWidth(), context.depthHeight(), P2D);

    for (int x = 0; x < context.depthWidth(); x++){
      for (int y = 0; y < context.depthHeight(); y++){
        color c = sceneImg.pixels[x+y*context.depthWidth()];
        if (red(c) == green(c) & green(c) == blue(c)){
          maskImg1.pixels[x+y*context.depthWidth()] = cBlack; // mask black
          maskImg2.pixels[x+y*context.depthWidth()] = cWhite; // mask white
        }else{
          maskImg1.pixels[x+y*context.depthWidth()] = cWhite; // mask black
          maskImg2.pixels[x+y*context.depthWidth()] = cBlack; // mask black
        }
      }
    }
    maskImg1.updatePixels();
    maskImg2.updatePixels();

    maskedImg1 = loadImage("screenshot.jpg");
    maskedImg2 = loadImage("screenshot.jpg");

    maskedImg1.mask(maskImg1);
    maskedImg2.mask(maskImg2);

    background(maskedImg2);
    image(maskedImg1, 0, 0, context.depthWidth(), context.depthHeight());
    blend(maskedImg2, 0, 0, context.depthWidth(), context.depthHeight()
                    , 0 ,0, context.depthWidth(), context.depthHeight(), SCREEN);

    mm.addFrame();
  }
}


void keyPressed() {
  if (key == ' ') { 
  mm.finish(); 
  println("save movie."); 
  exit();
  }
}

関連記事