Processing, simple-openni の覚え書き(不完全)

初版作成  2014.2
最終更新  2018.01.24


◆◆ Processing 1.x.y のエディタの設定 ◆◆

processing-1.x.y/lib/theme/theme.txt を書き換える。

変更点は以下の通り

editor.fgcolor = #ffffff
editor.bgcolor = #000000

editor.linehighlight.color=#404040

editor.caret.color = #ffffff

editor.selection.color = #ffff00

editor.keyword1.style = #ff0000,plain
editor.keyword2.style = #ff0000,plain
editor.keyword3.style = #ff0000,bold

editor.literal1.style = #ffff00,plain
editor.literal2.style = #ffff00,plain

editor.operator.style = #ffffff,plain

editor.comment1.style = #00ffff,plain
editor.comment2.style = #00ffff,plain

私が使っている processing-1.2.1 用の theme.txt は ここ

◆◆ Processing 2.1.1 のエディタの設定 ◆◆

日本語の表示方法は以下の通り

「File」→「Preferences」

Editor and Conaole font を「MS ゴシック」に設定
Editor font size は 16 か 18
Use smooth text in editor window のチェックを外す

しかし、全角と半角で別のフォントを選ぶことができない。
MS ゴシックは見づらい。日本語ありで MS ゴシックを使うか、
日本語は諦めて Consolas を使うかどちらか。

デフォルトの設定は白地なのでまぶしい。

色の設定は「ファイル」→「Preferences」ではできない。
設定ファイルを直接編集する。

設定は 2 箇所に分散している。

processing-2.1.1\lib\theme.txt
C:\Users\yabu\AppData\Roaming\Processing\preferences.txt

効果が不明の箇所もあるが、
以下のように設定した。

   -------- theme.txt ---------

editor.fgcolor = #ffffff
editor.bgcolor = #000000

editor.linehighlight.color=#403030  カーソルがある行の色

editor.caret.color = #ffffff        カーソルの色

editor.selection.color = #505050            コピー時の領域選択
editor.brackethighlight.color = #ff8800     [ ] 入力時  対応するカッコを囲む色

   -------- preferences.txt ---------

editor.window.height.default=1100
editor.window.width.default=800

editor.token.comment1.style=#ffff00,plain
editor.token.comment2.style=#ffff00,plain
editor.token.function1.style=#ff0000,plain
editor.token.function2.style=#ff0000,plain
editor.token.function3.style=#00ffff,plain
editor.token.function4.style=#00ffff,bold
editor.token.invalid.style=#e0e0e0,bold       <--- ' や " の後の文字
editor.token.keyword1.style=#ffff00,plain
editor.token.keyword2.style=#ffff00,plain
editor.token.keyword3.style=#ffff00,plain
editor.token.keyword4.style=#ffff00,plain
editor.token.keyword5.style=#ffff00,plain
editor.token.label.style=#e1e1e1,bold
editor.token.literal1.style=#00ffff,plain
editor.token.literal2.style=#00ffff,plain
editor.token.operator.style=#00ffff,plain


◆◆ プログラムの実行と終了 ◆◆

実行   ctrl + R
終了   ESC


◆◆ プログラムの基本構成 ◆◆

void setup(){

}

void draw(){

}

setup() は開始時に 1 度だけ呼ばれる。
draw() はデフォルトでは 1 秒間に 30 回呼ばれる。

frameRate(5)  とすると、1 秒間に 5 回。


◆◆ 変数 ◆◆

型宣言が必要

int      整数(2 バイト)
long     整数(4 バイト)micros() は unsigned long で受ける
float    倍精度実数
char     1文字
String   文字列
PVector  ベクトル
PImage   画像

>> 配列

int[]    a;
String[] str;

a.length    配列の要素数


◆◆ 文字処理 ◆◆

String[] data = split(line,'\t');


◆◆ コンソールへ出力 ◆◆

print("Hello");       改行なし
println("Hello");     改行あり

print("i = " + i);    VBS と同じ

フォーマット付
String line = nf(i, keta)          桁数
String line = nf(f, left, right)   小数点の左側、右側の桁数


◆◆ ファイルへ出力 ◆◆

String[] lines;
PrintWriter fp;

fp = createWriter("a.txt");

lines[0] = "a = " + nf(f,4,1);
lines[1] = "b = " + nf(f,4,1);

fp.println(line[0]);
fp.println(line[1]);

fp.flush();
fp.close();

あるいは次の 1 行で済ませることもできる。

saveStrings("a.txt",lines);


◆◆ ファイルから入力 ◆◆

float[] x,y;

String[] lines = loadStrings("a.txt");
for( int i = 0; i < lines.length; i++){
  String[] data = split(lines[i],' ');
  x[i] = data[0];
  y[i] = data[1];
}


◆◆ キーイベント、マウスイベント ◆◆

key, mouseX, mouseY はグローバル変数のようだ

void keyPressed(){
  if ( key == 's' ){
    output.flush();
    output.close();
  }
}

void keyReleased(){
  if ( key == 'a' ){

  }
}

void mousePressed(){
  x = mouseX;
  y = mouseY;
}


◆◆ 時刻 ◆◆

  int h = hour();
  int m = minute();
  int s = second();


◆◆ 図形描画 2 次元 ◆◆

size(640,480)    図形は xy の 2 次元で指定して描く
                 左上が (0,0)  右下が (width, height)
                 size() は setup() の中で定義する。

stroke(r,g,b)    線色
strokeWeight(w)  線幅
noStroke()       線なし

fill(r,g,b)      塗りつぶし色
noFill()         塗りつぶしなし

smooth()         アンチエイリアスあり
nosmooth()       アンチエイリアスなし

translate(x,y)   平行移動
point(x,y)                 点
line(x1,y1,x2,y2)          線
rect(x,y,width,height)     長方形
ellipce(x,y,width,height)  楕円


◆◆ 画像表示 ◆◆

image(kinect.rgbImage(),0,0)
image(kinect.rgbImage(),0,0,160,120)   左上に縮小表示

PImage image;
image = LoadImage("a.jpg");


◆◆ 文字 ◆◆

PFont font;

font = createFont("Arial",24)   // ここでの 24 は効かないようだ
textFont(font, 16);             // この 16 が効く

text("abc",x,y)

String s = Integer.toString(i);
text(s,x,y)

text(num,x,y)     num: int or float


◆◆ 図形描画 3 次元 ◆◆

size(width,height,P3D)   図形は xyz の 3 次元で指定して描く
                         size() は setup() の中で定義する。

perspective(angle, aspect, z_near, z_far)
    angle   視野角
    aspect  縦横比 通常は width/height
    z_near  カメラと手前のクリッピング平面の距離
    z_far   カメラと後方のクリッピング平面の距離

line(x,y,z,x2,y2,z2)     3 次元空間に線を引く  z:depth
translate(x,y,z)         平行移動
                         z の正方向が手前だから、z の正の値 translate すると
                         手前に近づく

rotateX(PI/6)            回転
rotateY(radians(90))     回転
box(width,height,depth)  中心位置は (0,0,0)  translate を使う


◆◆ 3 次元の座標 ◆◆

右:   x 正方向
下:   y 正方向
手前: z 正方向

setup: perspective(radians(45),float(640)/float(480),10,150000)
draw:  translate(320,240,0)

のとき正面に見える。


◆◆ simple-openni 基本 ◆◆

import SimpleOpenNI.*;
SimpleOpenNI kinect;

void setup()
{
  kinect = new SimpleOpenNI(this);
  kinect.enableDepth();
  kinect.enableRGB();
  kinect.setMirror(bool);  //  true と false の違いがない。バグ?
  background(200,0,0);
  size(kinect.depthWidth() + kinect.rgbWidth() + 10, kinect.rgbHeight());
}

void draw()
{
  kinect.update();
  image(kinect.depthImage(),0,0);
  image(kinect.rgbImage(),kinect.depthWidth() + 10,0);
}


depth 画像とrgb 画像の位置を合わせる方法

  kinect.alternativeViewPointDepthToImage();
  kinect.setDepthColorSyncEnabled(true);   <--- 不要?

rgb 画像はそのままで、depth 画像の xy 位置が
rgb 画像と同一になる。

マイクロソフトのドライバを使用すると、
赤外線画像は真っ黒になる。

  kinect.inableIR();
  kinect.irImage();

は使えないようだ。


◆◆ スケルトンの追跡(抜粋) ◆◆

void setup(){
  kinect.enableUser();
}

void draw(){
  PVector rpos = new PVector();
  PVector ipos = new PVector();

  int[] userList = kinect.getUsers();
  for ( i = 0; i < userList.length; i++){
    if ( kinect.isTrackingSkeleton(userList[i]) ){
       kinect.getJointPositionSkeleton(userList[i], 1, rpos);
       kinect.convertRealWorldToProjective(rpos, ipos);
    }
  }
}

void onNewUser(SimpleOpenNI curkinect, int userId)
{
  println("onNewUser - userId: " + userId);
  kinect.startTrackingSkeleton(userId);
}

void onLostUser(SimpleOpenNI curkinect, int userId)
{
  println("onLostUser - userId: " + userId);
}

void onVisibleUser(SimpleOpenNI curkinect, int userId)
{
//  println("onVisibleUser - userId: " + userId);
}

情報処理学会誌 2012.9 p.950 掲載の
サンプルプログラムは以下のようになっている。

void onNewUser(int userId)
{
  kinect.requestCalibrationSkeleton(userId, true);
}

void onEndCalibration(int userId, boolean successfull){
  if ( successfull ){
    kinect.startTrackingSkeleton(userId);
  }
}


◆◆ simple-openni  画像 ◆◆

   PImage rgbImage = kinect.rgbImage();
   PImage depthImage = kinect.depthImage();
   PImage userImage = kinect.userImage(); // ユーザー領域を着色した depth 画像

   index = y * kinect.rgbHeight() + x;
   color pixelColor = rgbImage.pixels[index]

   int[] rgbMap = kinect.rgbMap();
   int[] depthMap = kinect.depthMap();  単位は mm    0-255 ではない
   int[] userMap = kinect.userMap();    0: 何もなし  1-7: 人番号

   PVector[] realWorldMap = kinect.depthMapRealWorld
   realWorldMap[index].x    3D 空間の x 座標
                            z 座標は depthMap() と同一

◆◆ simple-openni  ユーザー、スケルトン ◆◆

スケルトン追跡開始
   kinect.startTrackingSkeleton(userId);

ユーザーの数

   int userCount = kinect.getNumberOfUsers();

認識中のユーザー番号のリスト

   int[] userList = kinect.getUsers();

ユーザーのスケルトン情報

重心
   PVector r_pos = new PVector();
   iret = kinect.getCoM(user_id, r_pos);

関節
   kinect.startTrackingSkeleton(userId);   最初に呼ぶ

   PVector r_pos = new PVector();
   fret = kinect.getJointPositionSkeleton(user_id, joint_no, r_pos);

   PVector r_vecto = new PVector();
   fret = kinect.getJointOrientationSkeleton(user_id, joint_no, r_vect);

   fret: その情報の信頼性 0〜1


◆◆ simple-openni 変換 ◆◆

距離画像の xy 位置から実空間の x y z 座標を得る

   PVector r_pos = kinect.depthMapRealWorld()[i];

空間の xyz 座標を距離画像の xy 座標に直す

   PVector r_pos = new PVector();
   PVector i_pos = new PVector();
   kinect.convertRealWorldToProjective(r_pos, i_pos)


◆◆ simple-openni 定数 ◆◆

SimpleOpenNI.*

0 SKEL_HEAD    顔の中心
1 SKEL_NECK    首の下
2 SKEL_LEFT_SHOULDER
3 SKEL_RIGHT_SHOULDER
4 SKEL_LEFT_ELBOW
5 SKEL_RIGHT_ELBOW
6 SKEL_LEFT_HAND
7 SKEL_RIGHT_HAND
8 SKEL_TORSO   へそのあたり
9 SKEL_LEFT_HIP
10 SKEL_RIGHT_HIP
11 SKEL_LEFT_KNEE  ひざ
12 SKEL_RIGHT_KNEE
13 SKEL_LEFT_FOOT
14 SKEL_RIGHT_FOOT


◆◆ PVector の使い方の注意 ◆◆

Processing は Java であり、演算子のオーバーロードはない。

>> 代入時の注意

  PVector a = new PVector(1,2,3);
  PVector b = new PVector();

  b = a;     ×      b と a が同一のものとなる
  b.set(a);  ○      a の要素を b に代入する

  a.x = 10;     もし b = a としていると、b.x は 10 となる。

>> 配列の使用方法(C++ とは異なるので注意)

  int NUM = 20;
  PVector[] loc = new PVector[NUM];  // new は関数外でも使える

  for(int i = 0; i < NUM; i++){
    loc[i] = new PVector();      // new は配列の要素数だけ実行
  }


◆◆ 型変換 ◆◆

C の文字列処理の関数が使える

int i;
char str[100];

char ---> int     sscanf(str,"%d", &i);
int ---> char     sprintf(str,"%d", i);

◆◆ 文字単位の処理 ◆◆

c の関数 strcpy などがそのまま使える。

ex.「最初の 2 文字」と「2 文字目以降」を、それぞれ数値として取り出す。

buf[0] = str[0];
buf[1] = str[1];
buf[2] = '\0';

strcpy(buf2, &str[2]);