Thanks Driven Life

日々是感謝

Kinect for Xbox360 + OpenNI2 + OSX 10.8 が「とりあえず」動くまで

環境

びるどー

事前準備 びるどー

$ brew install libfreenect --universal # universal 無いと駄目

OpenNI2 びるどー

本家 OpenNI/OpenNI2 ではなく、 OpenNI2-FreenectDriver を使う

$ git clone git://github.com/piedar/OpenNI2-FreenectDriver.git
$ cd OpenNI2-FreenectDriver
$ LDFLAGS="-L$(brew --prefix)/lib" \
  CFLAGS="-I$(brew --prefix)/include -I$(brew --prefix)/include/libfreenect" \
  make

じっこうー

$ cd Bin/x64-Release
$ ./SimpleViewer

まとめ

よくわかんないんですが、本家 OpenNI2 は

  • Windows 環境であれば Kinect for Windows SDK
  • それ以外だと ASUS Xtion 系

だと動くらしくて、しかし私は Kinect for Xbox360 しか持ってないので 泣く泣く OpenNI2-FreenectDriver に手を出してみたら一応動いたということでした。

本家に追従してくれると嬉しいけど、それよりは Xtion PRO Live 買った方がいい気もする。

Muscle based slideshow system "Musclide"

This article is the 10th of JavaFX Advent Calendar 2012.

here has been written in Japanese.

What's "Musclide" ?

Musclide (Muscle + Slide) is slideshow tool. source code gongo/Musclide · GitHub

demo movie.

Musclide demo from gongo on Vimeo.

  1. [00:01] The detection of the speaker begins when press the "Entering" button.
  2. [00:05] After the speaker has been detected, stick person appears.
    • stick person connected with movement of speaker
  3. [00:14] Operate the slide by posing the following four.

    Posing

  4. [00:53] Demonstrates using the actual slide.

  5. The rest of the time, enjoy the muscle presentation.

Steps

0. requirement

1. Install OpenNI/Sensor/NITE

2. IDE

Take your pick. I use IntellijIDEA.

Challenged

Previously , I made tool to operate the slide using muscle.


Tython Lightning Talk

(Speaker of this video is me. I'm talking in Japanese.)

This tool can be to simulate the keyboard events by the movement of the body.

  • "left jab" → "right arrow"
  • "right straight" → "left arrow" , etc...

This time, I did not divert this tool. Because It's a good chance to use the specific features of JavaFX (Property, Bind, Event).

Property & Binding

I was used Property and Binding to monitor the movement of the speaker in Musclide. You can see from the following code. (I wrote comment in Japanese. sorry.)

  1. Musclide/src/musclide/sensor/Speaker.java at master · gongo/Musclide · GitHub
    • Set the coordinates of body (head, left hand, etc..) to DoubleProperty.
  2. Musclide/src/musclide/sensor/MuscleRadar.java at master · gongo/Musclide · GitHub
    • Binding to javafx.scene.shape.Line property from coordinates properties.
  3. Stick person that connected with movement of speaker is now complete!

Custom Event

I made "MuscleEvent" similar to KeyEvent and MouseEvent. This event is generated when posing specific detected.

First, it is a code that defines the MuscleEvent class.

package musclide.event;

import javafx.event.Event;
import javafx.event.EventType;


public class MuscleEvent extends Event {
    public static final EventType<MuscleEvent> ANY
            = new EventType<MuscleEvent>(Event.ANY, "Detect Muscle Posing");

    public enum Pose {
        NEXT,
        PREV,
        FIRST,
        LAST,
        STANDBY
    };

    public Pose pose = Pose.STANDBY;

    public MuscleEvent() {
        super(ANY);
    }
}

code Musclide/src/musclide/event/MuscleEvent.java at master · gongo/Musclide · GitHub

Second, it is a code that generates (fire) MuscleEvent.

TimelineBuilder.create()
        .cycleCount(Timeline.INDEFINITE)
        .keyFrames(
                new KeyFrame(
                        new Duration(1000), // Check the pose every second
                        new EventHandler<ActionEvent>() {
                            @Override
                            public void handle(ActionEvent event) {
                                if (speaker == null || !speaker.isTracking()) {
                                    return;
                                }
 
                                if (speaker.owataPoseDetected()) {
                                    firePosingDetected(MuscleEvent.Pose.NEXT);
                                } else if (speaker.hangerPoseDetected()) {
                                    firePosingDetected(MuscleEvent.Pose.PREV);
                                } else if (speaker.showtimePoseDetected()) {
                                    firePosingDetected(MuscleEvent.Pose.FIRST);
                                } else if (speaker.kakashiPoseDetected()) {
                                    firePosingDetected(MuscleEvent.Pose.LAST);
                                } else {
                                    firePosingDetected(MuscleEvent.Pose.STANDBY);
                                }
                            }
                        }
                )
        ).build().play();

private void firePosingDetected(MuscleEvent.Pose pose) {
        MuscleEvent event = new MuscleEvent();
        event.pose = pose;
        Event.fireEvent(this, event);
}

public final void setOnPosingDetected(EventHandler<MuscleEvent> eventHandler) {
        this.addEventHandler(MuscleEvent.ANY, eventHandler);
}

code Musclide/src/musclide/Ring.java at master · gongo/Musclide · GitHub

Final, it is the code to catch MuscleEvent.

ring.setOnPosingDetected(new EventHandler<MuscleEvent>() {
    @Override
    public void handle(MuscleEvent muscleEvent) {
        switch (muscleEvent.pose) {
            case NEXT:
                next();
                break;
            case PREV:
                prev();
                break;
            case FIRST:
                first();
                break;
            case LAST:
                last();
                break;
            case STANDBY:
            default:
                standby();
        }
    }
});

code Musclide/src/musclide/slide/Slide.java at master · gongo/Musclide · GitHub

Supplementation : OpenNI and Java

Conclusion

Let's muscle presentation!!

Supplementation : Motivation for English Translation

肉体系スライドショーツール「Musclide」を作りました

これは JavaFX Advent Calendar 2012 10日目の記事となっております。

追記 いろいろあったので英訳 ver も書きました http://gongo.hatenablog.com/entry/2012/12/13/213720

経緯

去る11/24、JavaKueche で行われた JavaOne2012 報告会 & Java における HTML5 勉強会 に参加したのですが、その懇親会にて @ さんとお話しする機会がありました。

俺「少し前に ScalaFX とか少しだけ触ったことあります。」
寺田さん「そうなんですか!!ありがとうございます!!」
俺「え、一日しか触ったことな…」
寺田さん「ありがとうございます!!ありがとうございます!!」


そんなこんなで JavaFX をよくわかってない僕が「やってみた・作ってみた」記事で埋めようとした結果できたのが「Musclide」です。 パク……ネタ元は @ さんの JavaFX でプレゼンツール - JavaFX in the Box です!

上の記事で、skrb さんがこう仰っていました。

まずはじめに重要なのが、プレゼンツールとコンテンツは切り離して考えるべきということ。 コンテンツ側で表現しなくてはいけないことと、プレゼンツールで実装しなくてはいけないことをはっきりさせないといけません。

その通りだと思います。だから僕もこう言います。

コンテンツ側で表現しなくてはいけないことと、プレゼンツールで実装しなくてはいけないことと、己の肉体の動きをはっきりさせないといけません。

Musclide とは

Muscle + Slide です。その名の通り「肉体でスライド」するツールです。 ソースコードは公開してあります。 gongo/Musclide · GitHub

どういったものかは実際の動きを見ていただきたいと思います。

Musclide demo from gongo on Vimeo.

  1. 「Entering」ボタンを押すとプレイヤー(今回はスライドショーということで「Speaker」とします)の認識が始まります。
  2. 無事認識されると、Speaker の動きが棒人間に反映されます。
  3. 各種ポーズを取ることで、スライドの動きを制御できます。(画像用意するのめんどかったのでご勘弁)

     次(右)に進む
      \○/
       |
      / \

    先頭に戻る || | / \

    前(左)に戻る _ ○ _ | |  | / \

    最後に進む ○ -- | -- / \

  4. 00:53 からは実際のスライドを使ってのデモです。動きの確認用に使っていた領域をクリックして、スライドショーしたい画像が置かれてるディレクトリを指定します。

  5. 残りの時間、肉体派プレゼンとはどういったものかをご確認下さい。

実際に使うまでの流れ

0. 準備

  • Kinect
    • Xtion でもいいと思いますが、もってないので検証できてません。まあ大丈夫でしょう
  • 動ける空間
  • Java/JavaFX
    • 私は Java7u9 をインストールしたのですが、このバージョンになると JavaFX も一緒についてくるので 特に問題ない場合は 7u9 を入れちゃいましょう。

1. OpenNI/Sensor/NITE のインストール

Kinect を使ったシステムを作る場合、OpenNI あるいは Kinect for Windows SDK を使うのが主流だと思います。私が普段触っている開発環境は OS X なので、 OpenNI を使いました。

homebrew を使ってる方は [https://github.com/totakke/openni-formula:title] を使うほうが手っ取り早いです。 MacPorts とか、あるいは Ubuntu 等の Linux の皆様は [http://www.kinect-wiki.info/OpenNI-%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB:title] をご覧になればどうにかなります。がんばって!

2. 開発環境の準備

IntellijIDEA を使いました。理由は使ったことなかったからです。 あとはソースコードとってきたり、ビルド設定したり、がんばってください!!

せっかくなので私がひっかかった所を挙げます。

  • homebrew でデフォルトの場所 ( /usr/local ) じゃない所にインストールしている ( $HOME/.homebrew ) ため、 dylib や jar が上手く呼ばれなかった。そういう方々は

    • 実行時の VM パラメータで -Djava.library.pathorg.OpenNI.jar の場所を教えてあげたり
    • 環境変数(DYLD_FALLBACK_LIBRARY_PATH 等)で libOpenNI.dyliblibOpenNI.jni.dylib の場所を教えてあげたり

    すると上手くいくかもしれません。OpenNI のインストールは終わったのに、実行する時に "Failed: Can't create any node of the requested type!" って出るんだけど!って人は↑の可能性が高いです。

やってみたかったところと、やってみたこと

実は、Java じゃなく C++ で同じようなツールを作ったことがあります。 といっても、今回のように GUI から触ったわけではなく、 OS X の標準機能であるプレビューで PDF を表示したあと、 キーボードのイベントを体のうごきでシュミレートするってやつです。


Tython Lightning Talk

詳しくはこちらをお読みください。

  • [http://d.hatena.ne.jp/gongoZ/20110825/1314280053:title]
  • [http://d.hatena.ne.jp/gongoZ/20110829/1314576794:title]

この時作ったやつをだいたい流用したのですが、 せっかく JavaFX を使ったわけなので、JavaFX らしい(というか私が初めて触るもの)機能を使おうと思いました。

Property & Binding

当初はもうなんのこっちゃわからなかったんですが、使ううちになんとなくわかってきました。 と同時に、「これを使えば Speaker の動きをひたすら監視してくれるから、Speaker の描画簡単になるのでは?」と考え、 実際にできたのがこんなコードです。(長いのでリンクだけ)

  • [https://github.com/gongo/Musclide/blob/master/src/musclide/sensor/Speaker.java:title]
  • [https://github.com/gongo/Musclide/blob/master/src/musclide/sensor/MuscleRadar.java:title]

「Speaker.java」で各部位のポイントを DoubleProperty として保持し、そいつを 「MuscleRader.java」が持つ Line の各プロパティに bind してあります。 こうすると、Speaker の動きがそのまま Line に反映されて棒人間の完成です。

Custom Event

さきほどの動画で紹介した、パンチを打って動かすver では

  1. 無限ループ
  2. ↑ の中で、人があるモーションを行ったかをチェック
  3. モーションが検出されたら「右」という処理を実行

みたいなことをしています。Musclide でも基本的には同じなんですが、 ただ同じにするだけでは面白くなく。

JavaFX みたいなイベント駆動のあれこれを構築した経験があまり無い私としては、 せっかくなので「『こういうポーズした』というイベントを発行する・捕まえる」という一連の流れを やってみようと思いました。そんなわけでそれ用のイベントを作ってみました。

package musclide.event;

import javafx.event.Event;
import javafx.event.EventType;


public class MuscleEvent extends Event {
    public static final EventType<MuscleEvent> ANY
            = new EventType<MuscleEvent>(Event.ANY, "Detect Muscle Posing");

    public enum Pose {
        NEXT,
        PREV,
        FIRST,
        LAST,
        STANDBY
    };

    public Pose pose = Pose.STANDBY;

    public MuscleEvent() {
        super(ANY);
    }
}

Musclide/src/musclide/event/MuscleEvent.java at master · gongo/Musclide · GitHub

特に複雑なことをしているわけではないんですが。むしろこれであってますか?

イベントを発行する部分は以下です

// あるメソッドの中
TimelineBuilder.create()
        .cycleCount(Timeline.INDEFINITE)
        .keyFrames(
                new KeyFrame(
                        new Duration(1000), // 1秒毎にポーズ確認
                        new EventHandler<ActionEvent>() {
                            @Override
                            public void handle(ActionEvent event) {
                                if (speaker == null || !speaker.isTracking()) {
                                    return;
                                }
 
                                if (speaker.owataPoseDetected()) {
                                    firePosingDetected(MuscleEvent.Pose.NEXT);
                                } else if (speaker.hangerPoseDetected()) {
                                    firePosingDetected(MuscleEvent.Pose.PREV);
                                } else if (speaker.showtimePoseDetected()) {
                                    firePosingDetected(MuscleEvent.Pose.FIRST);
                                } else if (speaker.kakashiPoseDetected()) {
                                    firePosingDetected(MuscleEvent.Pose.LAST);
                                } else {
                                    firePosingDetected(MuscleEvent.Pose.STANDBY);
                                }
                            }
                        }
                )
        ).build().play();

private void firePosingDetected(MuscleEvent.Pose pose) {
        MuscleEvent event = new MuscleEvent();
        event.pose = pose;
        Event.fireEvent(this, event);
}

public final void setOnPosingDetected(EventHandler<MuscleEvent> eventHandler) {
        this.addEventHandler(MuscleEvent.ANY, eventHandler);
}

Musclide/src/musclide/Ring.java at master · gongo/Musclide · GitHub

else-if 連射は心を痛めながら書きました。心を痛めながら書きました。

最後に、イベント検知はこちら

ring.setOnPosingDetected(new EventHandler<MuscleEvent>() {
    @Override
    public void handle(MuscleEvent muscleEvent) {
        switch (muscleEvent.pose) {
            case NEXT:
                next();
                break;
            case PREV:
                prev();
                break;
            case FIRST:
                first();
                break;
            case LAST:
                last();
                break;
            case STANDBY:
            default:
                standby();
        }
    }
});

Musclide/src/musclide/slide/Slide.java at master · gongo/Musclide · GitHub

これでしっかりと「オワタポーズをした」というイベントが発生→ next() が実行、ということができました。簡単!!

忘れてたけど OpenNI と会話してる部分

まとめ

がががっと説明したので不足分はたくさんあるんですが、いかがでしたでしょうか。あなたも肉体派になりませんか?

oniファイル再生時に GetAlternativeViewPointCap().SetViewPoint() でエラーが出て困ってた

原因わかったあとは「そりゃあそうだよ」って思った。

経緯

oni ファイルを再生するときは、例えば

xn::Context context;
xn::Player player;
context.Init();
context.OpenFileRecording("gongo.oni", player)

みたいなことをしていますが、その後の処理でエラーになります。

xn::DepthGenerator depth;
xn::ImageGenerator image;
context.FindExistingNode(XN_NODE_TYPE_IMAGE, image);
context.FindExistingNode(XN_NODE_TYPE_DEPTH, depth);
depth.GetAlternativeViewPointCap().SetViewPoint(image); // ← ここでエラーステータスが返される

エラー内容を見ると「The node is locked for changes!」でした。

推測

原因を予想すると

  • GetAlternativeViewPointCap().SetViewPoint() は depth と image のズレを直してくれる
  • 上記では xn::Context では oni ファイルを再生する感じになってる
    • 再生しない場合はエラーにならない
  • 再生しているデータに対してのズレの修正はできない

勘です。
おそらくドキュメントに載ってるんですかね。ちゃんと探してないです。あるといいな。

補足

ちなみに SetViewPoint() が使えるかどうかってのを確認するには

depth.IsCapabilitySupported(XN_CAPABILITY_ALTERNATIVE_VIEW_POINT)

を実行して TRUE になるかどうかです。