workshop/processing_and_opencv/main

環境をつくる

Processing2.0 でOpenCVが動作する環境を作ります.Processing2.0をダウンロード/インストール後,Processingを立ち上げ, Import LibraryからOpenCVを検索し,OpenCVをインストールします.しかしこれだけではサンプルを動かすことができません. 別途OpenCVパッケージをインストールする必要があります.また,各Exampleも修正や追記をしないとうまく動作しないものが ほとんどなので,注意が必要です.では次の手順を踏んでください.

  1. Processing2をダウンロード
  2. Processingメニュー -> Sketch -> Import Library -> Add Library を選択
  3. 検索窓にOpenCVと記入.見つかったOpenCVライブラリをインストールする
  4. Processing(java)用OpenCVライブラリの提供元ウェブサイトへいく
  5. ウェブサイトのInstallation Instructionsの説明文の中に, MacOSXの場合は opencv-framework-1.1.dmgをインストールしてくれとあるので, ダウンロード後,パッケージ(OpenCV Framework.pkg)をインストールする.
  6. インストール後,Processingを立ち上げ,ライブラリ提供元ウェブサイトにあるいくつかのsampleを実行してみましょう.

サンプルを動かしてみる

いくつかのサンプルはそのまま動く場合がありますが,エラーや機能の実装がされていないものが含まれていました.そこを注意しながら サンプルを動かします.

顔認識

HAARトレーニングデータを利用した顔認識プログラムを動かしてみましょう.ライブラリ提供元ウェブサイトで提供されているコード 以外に修正をいれたプログラムを下記に記述します

sample.1: 顔認識
import hypermedia.video.*;
import java.awt.*; // Only just adding this line. 

OpenCV opencv;
void setup() {
    size( 320, 240 );
    opencv = new OpenCV(this);
    opencv.capture( width, height );
    opencv.cascade( OpenCV.CASCADE_FRONTALFACE_ALT );    // load the FRONTALFACE description file
}

void draw() {
    opencv.read();
    image( opencv.image(), 0, 0 );

    // detect anything ressembling a FRONTALFACE
    Rectangle[] faces = opencv.detect();
    
    // draw detected face area(s)
    noFill();
    stroke(255,0,0);
    for( int i=0; i < faces.length; i++ ) {
        rect( faces[i].x, faces[i].y, faces[i].width, faces[i].height ); 
    }
}

練習

見つかった顔領域に対して,適当な画像を差し替えてみましょう.

ROI: Region Of Interest

ROIとは現在の画像ファイルから興味をもつ特定の領域のことです.例えばカメラ映像から顔画像を取得したい場合, 顔を示す画像領域部分がROIとなります.opencvにはROI関数が用意されており,これを使うと任意の場所/ピクセルを手軽に取得できます.

sample.2: 指定領域だけ閾値設定をする
import hypermedia.video.*;
import java.awt.*;

OpenCV opencv;

void setup() {
    size( 320, 240 );
    opencv = new OpenCV(this);
    opencv.capture( width, height );
}

void draw() {
    opencv.read();
    image( opencv.image(), 0, 0 );
    opencv.ROI(mouseX, mouseY, 100, 100);
    opencv.threshold(80);
    image(opencv.image(), 0, 0);    
}

練習

上記のコードを,指定領域だけ閾値設定するものから,ブラーエフェクト(ぼかし効果)に変更することで,顔の部分だけリアルタイムに ぼかしをいれるプログラムに変更してみましょう.

輪郭検出

ROIを利用して,画像上で注目スべき場所がわかった場合,顔以外を対象とした場合,これまでのサンプルでは 特定が困難です.画商処理では一般的に画像を2値化し,輪郭検出を利用してオブジェクトのありなしを判断する やり方がよく用いられます.そこで,Blobing(輪郭検出)を利用してみます.

sample.3: 輪郭を検出して,その領域や座標を表示する
import hypermedia.video.*;
import java.awt.*;

OpenCV opencv;

void setup() {
  size( 320, 240 );
  opencv = new OpenCV( this );
  opencv.capture( 320, 240 );
}

void draw() {  
  background(255);
  opencv.read();
  opencv.convert(OpenCV.GRAY);
  opencv.threshold(50);   
  image(opencv.image(), 0,0, 320, 240);
    

  Blob[] blobs = opencv.blobs( 100, width*height/2, 100, true, OpenCV.MAX_VERTICES*4 );

  for ( int i=0; i < blobs.length; i++ ) {
    stroke(255, 0, 0);
    beginShape();
    for ( int j=0; j < blobs[i].points.length; j++ ) {
      vertex( blobs[i].points[j].x, blobs[i].points[j].y );
    }
    endShape(CLOSE);

    noFill();
    stroke(0, 0, 255);
    Rectangle rect  = blobs[i].rectangle;
    rect(rect.x, rect.y, rect.width, rect.height);
    noStroke();
    fill(0, 255, 0);
    text("("+rect.x+",("+rect.y+")", rect.x, rect.y);
  }
}

練習

検出した領域だけ白黒で表示してみましょう

課題

デジタルサイネージを作ります. OpenProcessing 2D Simulations Collectionから好きなものを選び,デジタルサイネージに 利用してみましょう.広告コンテンツはみなさんで適切なものを設定して下さい.

見本:画面変化箇所の検出とエフェクト

映像変化箇所に応じて波紋CG処理するプログラムは下記の通りとなります.

waterCamera.pde
import hypermedia.video.*;
import java.awt.*;

PImage img;
waterDrop banana = new waterDrop(640, 480);
OpenCV opencv;

void setup() {
  size(640, 480);
  frameRate(60);
  opencv = new OpenCV(this);
  //opencv.movie("./data/bbrattle.mov",640, 360);
  opencv.capture(640, 480); 
  loadPixels();
  smooth();
}

void draw() {
  opencv.read();
  image(opencv.image(), 0, 0, width, height);
  opencv.absDiff();
  opencv.remember();
  opencv.threshold(50);
  Blob[] blobs = opencv.blobs( 100, width*height/2, 10, true, OpenCV.MAX_VERTICES*4 );

  for ( int i=0; i < blobs.length; i++ ) {
    for ( int j=0; j < blobs[i].points.length; j++ ) {
      banana.disturb(blobs[i].points[j].x, blobs[i].points[j].y );
    }
  }
  banana.draw();
}
waterDrop.pde
/* OpenProcessing Tweak of *@*http://www.openprocessing.org/sketch/43543*@* */
class waterDrop {
  waterDrop(int width, int height) {
    hwidth = width>>1;
    hheight = height>>1;
    riprad=5; //test with 3

    size = width * (height+2) * 2;

    ripplemap = new int[size];
    ripple = new int[width*height];
    texture = new int[width*height];

    oldind = width;
    newind = width * (height+3);
  }
  void draw() {
    loadPixels();
    texture = pixels;

    newframe();

    for (int i = 0; i < pixels.length; i++) {
      pixels[i] = ripple[i];
    }

    updatePixels();
  }

  public void disturb(int dx, int dy) {
    for (int j=dy-riprad;j < dy+riprad;j++) {
      for (int k=dx-riprad;k < dx+riprad;k++) {
        if (j>=0 && j < height && k >= 0 && k < width) {
          ripplemap[oldind+(j*width)+k] += 2;   //test with 512
        }
      }
    }
  }

  void newframe() {
    //Toggle maps each frame
    i=oldind;
    oldind=newind;
    newind=i;

    i=0;
    mapind=oldind;
    for (int y=0;y < height;y++) {
      for (int x=0;x < width;x++) {
        short data = (short)((ripplemap[mapind-width]+ripplemap[mapind+width]+ripplemap[mapind-1]+ripplemap[mapind+1])>>1);
        data -= ripplemap[newind+i];
        data -= data >> 5;
        ripplemap[newind+i]=data;

        //where data=0 then still, where data>0 then wave
        data = (short)(1024-data);

        //offsets
        a=((x-hwidth)*data/1024)+hwidth;
        b=((y-hheight)*data/1024)+hheight;

        //bounds check
        if (a>=width) a=width-1;
        if (a<0) a=0;
        if (b>=height) b=height-1;
        if (b<0) b=0;

        ripple[i]=texture[a+(b*width)];
        mapind++;
        i++;
      }
    }
  }
  int size;
  int hwidth, hheight;
  int riprad;

  int ripplemap[];
  int ripple[];
  int texture[];

  int oldind, newind, mapind;

  int i, a, b;
}


Copyright (c) 2015 Tetsuaki BABA all rights reserved.