2012-12-30

Box-Muller法と正規乱数生成器

PRML上巻の序章を読んだ頃に、ガウス分布に肄う乱数発生器を作りたいと思っていたので作ってみた。つまり、平均値と標準偏差を引数に取る乱数生成関数である。Web検索するとBox-Muller法を使うのと、中心極限定理を使う方法があるのでそれぞれ調べてみた。

Box-Muller法を使う

Box-Muller法は次の式によって作られる2つの独立変数を使うらしい。
$\sqrt{-2 * log(α)} * sin(2πβ)$
$\sqrt{-2 * log(α)} * cos(2πβ)$
式だけ見てもイメージが湧かないので、Octaveを使ってサンプル数2000でテストしてみた。
check_box_muller.matlab
clear;
alpha = rand(1, 1000);
beta = rand(1, 1000);
val1 = sqrt(-2 * log(alpha)) .* sin(2 * pi * beta);
val2 = sqrt(-2 * log(alpha)) .* cos(2 * pi * beta);
hist([val1,val2], 20);


ちゃんと正規分布っぽい。Box-Muller法で生成した標準正規分布に標準偏差を掛けて、平均値を足せば完成。
"use strict";

/**
 * @param {Number} mean 平均値
 * @param {Number} std 標準偏差
 */
function gaussianRandom(mean, std) {
  return randByBoxMullerTransform() * std + mean;
}

// Box Muller法を使って標準正規分布N(0,1)を得る
var randByBoxMullerTransform = (function() {
  var vals = [];

  function calc() {
    var alpha = Math.random(),
        beta = Math.random();
    return [
      Math.sqrt(-2 * Math.log(alpha)) * Math.sin(2 * Math.PI * beta),
      Math.sqrt(-2 * Math.log(alpha)) * Math.cos(2 * Math.PI * beta)
    ];
  }

  return function() {
    vals = vals.length == 0 ? calc() : vals;
    return vals.pop();
  }
})();

// Module export
module.exports = gaussianRandom;

中心極限定理を使う

中心極限定理を利用すると、複数の一様乱数の和を使うだけで標準正規分布N(0,1)が得られる。
"use strict";

/**
 * @param {Number} mean 平均
 * @param {Number} std 標準偏差
 */
function gaussianRandom(mean, std) {
  return randByCentralLimitTheorem() * std + mean;
}
 
// 中心極限定理を使って標準正規分布N(0,1)を得る
function randByCentralLimitTheorem() {
  var v = 0;
  for (var i = 0; i < 12; i++) {
    v += Math.random();
  }
  return v - 6;
}
 
// Module export
module.exports = gaussianRandom;
こっちの方が圧倒的に簡単でびびる。

このエントリーをはてなブックマークに追加

2012-12-27

autojumpをMacとUbuntuに導入した

サーバーとMacで共通のzsh用dotfilesを使っているのだが割とうまくいった。zshとautojumpの連携は、zshのプラグインマネージャのantigenを使った。

autojumpのインストール

Mac

brew install autojump

Ubuntu

sudo apt-get install autojump

dotfilesの設定

自分のdotfilesリポジトリにantigenをsubmoduleとして追加

cd dev/dotfiles
git submodule add https://github.com/zsh-users/antigen.git antigen
git submodule init
git submodule update

.zshrcからantigenを呼び出し

~/.zshrc
source ~/dev/dotfiles/antigen/antigen.zsh
antigen-lib
antigen-bundle autojump
antigen-apply

j がaliasとして使われるので、jのaliasを変更しておく。


このエントリーをはてなブックマークに追加

2012-12-21

MacOS X Mountain Lionの通知センターを便利に使う

この記事はVOYAGE GROUP エンジニアブログ : Advent Calendar 2012の20日目である。特にテーマは何でも良いそうなので、Mac OS X Mountain Lionで追加された通知センターについて。

VOYAGE GROUPではグループウェアにサイボウズガルーン3を使っている。このサイボウズガルーン3、不便な事にMac用のリマインダークライアントが提供されていない。そこで、ミーティングをすっぽかさないためにもMacユーザーはリマインダーを各自実装する必要がある。
私はAPIを叩いて結果をGrowl通知させていたが、OSアップデートでこれが動かなくなった。単にGrowlをインストールするのを忘れていただけだが、通知センターがOSに追加されたのでGrowlはもう不要な気がする。

というわけで、通知センターでガルーンのスケジュール通知を受けられるようにしてみよう。私はObjective-Cが全く書けないので、ガルーンのAPIを叩く所はとりあえず現状のを使いまわして、JSON形式のファイルを配置するようにした。

通知センターに通知を送る

通知センターを使うのに一番手っ取り速いのは多分Cocoaなので、Xcodeでそれっぽいコードを書く。NSUserNotificationNSUserNotificationCenterを使えば良い。

通知を表示するだけなら通知を送った後にアプリケーションを終了さても良いが、通知をクリックした時に任意の操作をしたい場合はアプリケーションを生かしておく必要がある。ガルーンAPIを叩く部分のCocoa移植は後にして、とりあえず動くようになった。めでたしめでたし。



という訳で、せっかくエンジニアをやっているので、日々ハックできる所はハックしていきたいですね。


次は@pank7さんです。

このエントリーをはてなブックマークに追加

2012-12-12

はてなダイアリーから引越しました。

おそらくはてなダイアリーの方はもう更新しません(^-^)

旧ブログ
http://d.hatena.ne.jp/hagino_3000


今の所、2012年分の記事まで移行完了。

このエントリーをはてなブックマークに追加

2012-09-10

映像情報メディア学会誌に寄稿しました

2012年9月号に「Kinectの仕組みとナチュラルユーザインタフェース」という記事を書かせてもらいました。「拡張現実感技術の最前線」という連載物の一つです。

連載の他の担当は東大の暦本先生だったり凄い面々で、そこに自分が入ってるのは謎なんですが、よろしくおねがいます。


このエントリーをはてなブックマークに追加

2012-09-06

JavaScriptテクニックバイブルのレビューを手伝いました。

校正段階でレビューだけやらせてもらいました。若手IT勉強会のみなさんによる執筆。対象読者はJS中級者、2~3年目のエンジニアとの事。

JavaScriptテクニックバイブル ~効率的な開発に役立つ150の技JavaScriptテクニックバイブル ~効率的な開発に役立つ150の技
JSサポーターズ

技術評論社
売り上げランキング : 1582

Amazonで詳しく見る


テクニックバイブルシリーズなだけあっていろいろ載っている。足回りの開発ツールから、新しめのライブラリの使い方、JSの流儀まで。Node.jsについて3ページ割かれているのを除いてクライアントサイドの話。「現場のJSerが贈る」と表紙にある通り、古いIEでの嵌り所もちゃんと書いてあって親切。

良いなと思った所。次に挙げる4つの章
  • 4章:関数を活用する
  • 5章:ネットワーク通信を改善する
  • 6章:タイマーイベントを活用する
  • 8章:パフォーマンスを改善する

は賞味期限の長い普遍的なテクニックが詰まっている、1~2年目JSerは読んでおいて損は無いはず。タイマー、イベント伝播、通信周り、関数オブジェクトの解説が丁寧なので、その辺りをあまり理解しないでコードを書いている人にも良さそう。


このエントリーをはてなブックマークに追加

2012-07-07

横浜市税の使われ方を可視化するサービスをリリースしました



f:id:hagino_3000:20120707232838p:image:w640

先日のGLOCOMオープンデータ活用ハッカソンの成果です.私はアイデアソンの時に税金の使われ方を可視化するチームに参加しました.このチーム,やけに行動力のあるプロフェッショナルなメンバーに恵まれ,アイデアソンの直後から,データ入手等の準備を進めました.ハッカソン当日には英国の公共支出を扱った Where does my money go? をローカライズ,横浜市の予算データのコンバート,所得控除の要素を追加して http://spending.jp を公開できました.

オープンデータの動き

今回のオープンデータ活用ハッカソン,政府のオープンデータ戦略と切り離して語る事はできません.内閣の高度情報通信ネットワーク社会推進戦略本部(IT戦略本部)は今年の6月,政府のデータは原則公開.マシンリーダブルな形式でもって公開する.と方向性を示しました.



つまり,日本の政府・官公庁・自治体が持っていたデータがこれから公開され,営利・非営利問わずあらゆる事に利用が可能になるわけです.GLOCOMが主催したオープンデータ活用アイデアソン・ハッカソンは今後も続けたいとの事だったので,spending.jpの様な取り組みは増えてくるでしょう.

Open Spending

spending.jpは横浜市の平成24年度一般会計予算を利用しました.このデータを一旦Open Spendingに投入して,Open SpendingのAPIを叩いて取得したJSONをファイルに保存して使っています.


一旦Open Spendingに投入すると,会計年度を条件にデータを取得したり,集計したりできるので便利.APIはJSONPでも叩けるので,クライアントサイドJavaScriptだけでも使えます.

spending.jpの今後

ハッカソンで作ってそのまま終り,というわけでは無く,今後も運営チームで発展させていく予定です.


このエントリーをはてなブックマークに追加

2012-06-13

脳波ヘッドセットでハンズフリーウェブブラウジングシステムを作る

両手が塞がる食事中でも,快適にインターネットができる仕組みが求められている.そこで手元にあったEmotiv EPOC HeadsetとEmotiv SDK付属ツールを使って実験した.



手法

今回はeclipseを起動するのが面倒だったので,プログラミングはせず,SDK付属ツールだけで実験した.

マウス操作

EmotivControlPanelからMouse Emulaterというツールが起動できるので,これを使う.Mouse Emulaterはヘッドセットのジャイロセンサの値を使ってマウスカーソルを動かすツール.感度が良くブレも無いので思い通りにマウスカーソルを動かせる.

キーボード操作

SDK付属のEmoKeyを使う.EmoKeyはEmotiv SDKで取得した値を条件にキーボードイベントを生成できる.まずEmotiv SDKで取得できる値は次の通り.上手く翻訳できないので原文ままの単語も載せる.

感情 (それぞれ 0.0 ~ 1.0の値)

  • Engagement/Boredom
  • Frustration
  • Meditation
  • Instantaneous Excitement
  • Long Term Excitement

表情

  • Blink: まばたき
  • Wink: ウィンク (Right | Left)
  • Look: 視線の動き (Right | Left)
  • Brow: 額の動き (Rise | Furrow)
  • Smile: 笑顔
  • Clench: 歯を食いしばる
  • Smirk: 頬を吊りあげる (Right | Left)
  • Laugh: 笑い
  • Natural: 標準

カスタム(トレーニングデータ使用)

  • Push
  • Pull
  • Down
  • Up
使える値はEmotiv Composerを見るとだいたいわかる



食事中を想定しているので,口の周りの動きに影響を受けるClenchやSmirkは使えない.Blinkは使えそうだが,これは抑制が難しくしばしば誤発動する.個人的には額の動きが簡単だったので,これを左クリックにマッピングした.スクロールのためのjkキーはウィンクにした.

結果

Chrome拡張のkeyconfig等と組み合せると割と使える事がわかった,ドラッグも不可能では無くマウスの移動精度にストレスは感じない.逆に悪い点としては,味覚がまったく機能しなくなり,何を食べているのかわからなくなる.実験にはモスバーガーを用いたのだが,随分と勿体無い事をした.ハンズフリーインターネット時には100円程度のジャンクバーガーで十分だった.

番外編,感情の値を使った自動リブログ

せっかくの脳波ヘッドセットなので,感情の値も使ってみたい.なので,Instantaneous Excitement (瞬間的興味?) の値が上昇したらリブログするパターンもやってみた.即ち,自分の興味関心のあるコンテンツを自動でリブログしてくれる夢のシステムである.

設定はこんな感じ






Instantaneous Excitementの値が上下するのにやや時間がかかるため,高速でフィードを消化するような場面には適用できないが,何かに使えるのかもしれない.

次回はちゃんとEmotiv SDKを使ってコードを書いてやります.

このエントリーをはてなブックマークに追加

2012-05-28

新たなKineticデバイス「LEAP」の注文をするなど


彗星の如く登場して各界を賑わせているLEAPについて.

Leap Motion
https://live.leapmotion.com/index.html



D

公式サイトから読み取れる事

  • マウスの代りになるデバイスである
  • iPodサイズでUSBでコンピューターに接続して使う
  • 小売価格は69.99USドル
  • 既存の3Dセンサーの100倍以上の精度
  • MaxOS, Windows7, Windows8 に対応

今できる事

  • プレオーダー (発送は 2012年12月か2013年1月)
  • 開発者登録
日本への発送だと,送料込みで82.98USDになります.とりあえず一台注文して開発者登録をしておきました.SDKの入手には開発者登録が必要の様です.


このエントリーをはてなブックマークに追加

2012-04-16

Struct.js ver. 0.3をリリースしました

欲しかった機能が実装できたのでタグを切りました。

主な機能追加

  • ネストした構造のサポート
  • nullableオプションの追加
  • カスタムバリデーション関数のサポート
  • 設定で全チェックを無効化する機能を追加
  • 初期作成時の値チェックを追加
型チェックのためのコーディングをなるべく増やさない、というポリシーです。なので、素のObjectと同様に扱えるが必要なチェック機構は備えている、という物を目指しています。しかし副作用としてプロパティの有無でそれが何であるかを判定するダックタイピングは不可能になってしまったので、そこだけは Struct.getType(obj) を使います。

リリース用の設定

Proxyオブジェクトを生成しないので、通常のプロパティアクセスのコストだけになる。
Struct.configure({
  // 全てのチェック無効化
  'disable any check': true
});

バリデーション関数のサポート

Struct.define('Size', {
  width:  {type: 'number', cond: 'v >= 0'},
  height: {type: 'number', cond: function(v){return v >= 0} /*same as ↑*/}
});

var size = Struct.create('Size', {
  width: 0,
  height: 0
});

ネスト構造のサポート

Struct.create にネストしたObjectをそのまま食わせても大丈夫。
Struct.define('Position', {
  x: {type: 'number', nullable: false},
  y: {type: 'number', nullable: false}
});

Struct.define('Size', {
  width:  {type: 'number', nullable: false, cond: 'v >= 0'},
  height: {type: 'number', nullable: false, cond: 'v >= 0'}
});

Struct.define('Rect', {
  pos:  {type: 'struct:Position', nullable: false},
  size: {type: 'struct:Size', nullable: false},
  createdAt: {type: 'date', writable: false}
});

var rect = Struct.create('Rect', {
  pos: {x: 0, y: 0},
  size: {width: 100, height: 100},
  createdAt: new Date()
});

rect.pos.x = "200"; // => type mismatch error
rect.size.height = -1; // => condition formula validation error

今後の予定

次は関数呼び出しのパラメータチェックを実装する予定。



このエントリーをはてなブックマークに追加

2012-04-04

JavaScriptでrubyのmethod_missingを実装する

追記: Firefoxの実装で既に有る__noSuchMethod__に名前は合せた方が良い、というコメントを頂いたので名前を変えました。

何の役に立つか不明だけど書いてみた*1。Proxyでプロパティアクセスをフックして、存在しない場合は用意しておいた関数プロキシを返す。

上記の処理が書いてあるのはこの部分。
function enableMethodMissing(obj) {
  // 関数プロキシの作成
  var functionHandler = createBaseHandler({});
  functionHandler.get = function(receiver, name) {
  // プロパティアクセスの場合は何も返さない
    return function(){};
  }

  var calledProperty;
  var trapFn = Proxy.createFunction(functionHandler, function() {
    // 実行の場合は obj.methodMissing を呼び出す
    return obj.__noSuchMethod__(calledProperty, Array.prototype.slice.call(arguments));
  });

  // プロパティプロキシの作成
  var propertyAccessHandler = createBaseHandler(obj);
  propertyAccessHandler.get = function(receiver, name) {
    if (obj[name]) {
      return obj[name];
    } else {
      // 存在しないプロパティへのアクセスは関数プロキシを返す
      calledProperty = name;
      return trapFn;
    }
  }
  return Proxy.create(propertyAccessHandler);

// (略)
method_missing用の関数をわざわざProxy.createFunctionしているのは、プロパティアクセスの時はundefinedを返す、関数呼び出しの時はmethod_missing用の関数実行と処理を分けるため。
Proxyが何に使えるか、というのはこの動画がわかりやすかった。


D



*1:Firefox4以降 or JavaScriptの実験機能を有効にしたChromeでのみ動作します。





このエントリーをはてなブックマークに追加

2012-03-29

ChromeでECMAScript 6のProxyを有効にする

ECMAScript 6(候補)のProxyはChromeのデフォルト状態では使えない、前の記事で紹介したStruct.jsはProxyを使っているため、Chromeでチェック機構を有効にするには設定をいじる必要があります。

手順

アドレスバーからchrome://flagsを開く

f:id:hagino_3000:20120329024105p:image:w360

JavaScript の試験運用機能を有効にする の所で「有効にする」をクリック

f:id:hagino_3000:20120329024104p:image:w360

chromeを再起動

動作確認

こんなコードでOK

> typeof(Proxy); // => object
> typeof(Proxy.create); // => function

ただ、これだけではいろいろと足りない物があるので、同時にECMAScript 6のメソッドを補完してくれる es6-shim.js を使うと良いです。



このエントリーをはてなブックマークに追加

2012-03-28

JavaScriptで厳格な型チェックを行なうライブラリを書いた

(追記) Version 0.3をリリースしました。

先日のjava-ja温泉で「JavaScriptは独りでヒャッハーするのには良いけど、複数人で開発しだすと途端にカオスになって苦労する」という話になった。それに対する解の一つはClosure Compilerが採用したアノテーションによる型の定義と静的チェックだろう。それとは別のアプローチで何かできないかなと考えていたら、ECMAScript 6にObjectのプロパティアクセスをフックできる仕組み(Proxy.create)があったのを思い出した。Proxy APIを使ってみたら型チェックが効くC言語の構造体みたいな物が作れたのでライブラリ化した。


これを使うと何が良いかというと
obj = {};
obj.hogeFuga = new Foo();

// do something

obj.hoge_fuga = null; // 不要になったので解放 (タイプミス)
といった、動かしてもすぐには気づきにくいミスに例外を投げてくれる。Object.seal でも良いけど、未定義プロパティのreadに対しては何もしてくれないのが不満だったので、それも例外が飛ぶようにした。

使い方は次の通り。Object.defineProperties に似せた。
// 構造体の定義
Struct.define('Position', {
x: {type: 'number'},
y: {type: 'number'}
});

Struct.define('Square', {
name: {type: 'string', writable: false},
pos: {type: 'struct:Position'},
hidden: {type: 'boolean'}
});

// 作成
var sq = Struct.create('Square', {
name: 'mySquare',
hidden: true
});

// 通常のObjectと同様に扱える
sq.pos = Struct.create('Position', {x: 10, y: 20});
sq.hidden = false;
console.log(sq.name); // => mySquare
console.log(sq.pos.x); // => 10

// 以降の操作は全てチェックエラー
var name = sq.Name; // 未定義プロパティの読み取り
delete sq.Name; // 未定義プロパティのdelete
sq.visible = true; // プロパティの追加
sq.pos.x = "100"; // 型の不一致
sq.name = "hoge"; // 読み取り専用プロパティの変更
動作にはECMAScript 6 のProxyが必要なので、Firefox or 設定をいじったChromeじゃないと動かない。しかしProxyが無くても例外が飛ばないだけなので問題は無い、開発時にエラーが飛んでくれればそれで良いと考えているので。

(追記) ChromeでProxyを有効にする方法を書きました。

このエントリーをはてなブックマークに追加

2012-03-21

Node.jsのモジュールをC++で書く (ObjectとArrayを受け取るメソッド)

プリミティブな値を引数で受けとる場合よりもやや面倒。JavaScriptの仕様上どんな値や型でも受けとってしまうので、C++側のチェックコードがどんどん増えていく。

動作確認用のコードがこうだとする
var Test1 = require('./build/Release/Test1');

Test1.giveMeObject({
option1: 'This is config option!!',
option2: true
});

Test1.giveMeArray([1,2,3,4,5,6]);

モジュールのコードはこんな感じ
#include <node.h>
#include <v8.h>
#include <iostream>

using namespace v8;

Handle<Value> HandleObjectMethod(const Arguments& args) {
    HandleScope scope;

    // 第一引数の型がObjectかどうかをチェック
    if (!args[0]->IsObject()) {
        Local<String> msg = String::New("Argument must be Object type");
        ThrowException(Exception::TypeError(msg));
        return scope.Close(Undefined());        
    }

    // v8::Object型にキャスト
    Local<Object> obj = Local<Object>::Cast(args[0]);

    std::string strOption1 = "";
    bool bOption2 = "";
    {
        // Check Option1
        Local<Value> v = obj->Get(String::New("option1"));
        if (v.IsEmpty() || !v->IsString()) {
            Local<String> msg = String::New("Option1 is not specified or invalid type");
            ThrowException(Exception::TypeError(msg));
            return scope.Close(Undefined());                        
        } else {
            int length = v->ToString()->Length();
            char c[length];
            v->ToString()->WriteAscii(c);
            strOption1 += c;
        }
    }
    std::cout << "Option1 = " << strOption1 << std::endl;

    {
        // Check Option2
        Local<Value> v = obj->Get(String::New("option2"));
        if (v.IsEmpty() || !v->IsBoolean()) {
            Local<String> msg = String::New("Option2 is not specified or invalid type");
            ThrowException(Exception::TypeError(msg));
            return scope.Close(Undefined());                        
        } else {
            bOption2 = v->ToBoolean()->Value();
        }
    }
    std::cout << "Option2 = " << bOption2 << std::endl;

    return scope.Close(Boolean::New(true));
}

Handle<Value> HandleArrayMethod(const Arguments& args) {
    HandleScope scope;

    // 第一引数の型がArrayかどうかのチェック
    if (!args[0]->IsArray()) {
        Local<String> msg = String::New("Argument must be Array type");
        ThrowException(Exception::TypeError(msg));
        return scope.Close(Undefined());        
    }

    // v8::Arrayにキャスト
    Local<Array> arr = Local<Array>::Cast(args[0]);
    int length = arr->Length();
    std::cout << "Length:" << length << std::endl;

    for (int i = 0; i < length; i++) {
        // Arrayの各要素にアクセス
        Local<Value> v = arr->Get(i);

        // Something todo
    }

    return scope.Close(v8::Boolean::New(true));
}


void init(Handle<Object> target) {
    // メソッドのexport
    target->Set(String::NewSymbol("giveMeObject"), FunctionTemplate::New(HandleObjectMethod)->GetFunction());
    target->Set(String::NewSymbol("giveMeArray"), FunctionTemplate::New(HandleArrayMethod)->GetFunction());
}

NODE_MODULE(Test1, init)

実行結果
Option1 = This is config option!!
Option2 = 1
Length:6


このエントリーをはてなブックマークに追加

2012-03-11

Node.jsのモジュールをC++で書く (引数と戻り値)

引き続きv8.hを読みながらNodeモジュールを書く練習をする。

モジュールのメソッド定義ですが、名前の通りv8::Argumentsが引数の表現で、[]オペレータでアクセスするとv8::Value型のそれぞれの引数が得られる。型チェックはIsNumberやらIsStringといったメソッドはあっても、型を直接返してくれる物は無いのでやや面倒。

試しに、こんなモジュールを作ってみる。
#include <node.h>
#include <v8.h>
#include <iostream>

using namespace v8;

// 引数の数と、それぞれの型をチェックするメソッド
Handle<Value> ArgumentTest(const Arguments& args) {
    HandleScope scope;
    for (int i = 0; i < args.Length(); i++) {
        if (args[i]->IsString()) {
            std::cout << "String!!" << std::endl;
        } else if (args[i]->IsFunction()) {
            std::cout << "Function!!" << std::endl;
        } else if (args[i]->IsObject()) {
            std::cout << "Object!!" << std::endl;
        } else {
            // Number or Boolean or Date ... etc
        }
    }
    return scope.Close(v8::Number::New(args.Length()));
}

// 第一引数をそのままreturnするメソッド
Handle<Value> ReturnTest(const Arguments& args) {
    HandleScope scope;
    return scope.Close(args[0]);    
}

// メソッドのexport定義
void init(Handle<Object> target) {
    target->Set(String::NewSymbol("argumentTest"), FunctionTemplate::New(ArgumentTest)->GetFunction());
    target->Set(String::NewSymbol("returnTest"), FunctionTemplate::New(ReturnTest)->GetFunction());
}

NODE_MODULE(Test1, init)

呼び出し側のコード
var Test1 = require('./build/Release/Test1');

console.log("Check arguments...");
var result = Test1.argumentTest("str", function(){}, {hoge:100});
console.log(result + ' arguments passed');

console.log("");
console.log("Check return value type");
console.log(typeof(Test1.returnTest("str")));
console.log(typeof(Test1.returnTest(100)));
console.log(typeof(Test1.returnTest(new Date())));
console.log(typeof(Test1.returnTest(function(){})));

実行結果
Check arguments...
String!!
Function!!
Object!!
3 arguments passed

Check return value type
string
number
object
function


クラスメソッドしか持たないモジュールはこの書き方がわかれば作れそう。あと、コードを書く時はIDEの支援が欲しいので、Xcodeでソース開いてv8とnodeのコードをヘッダ検索パスに追加した状態で作業した方が楽できますね。

f:id:hagino_3000:20120311024001p:image

続きます

Node.jsのモジュールをC++で書く (ObjectとArrayを受け取るメソッド)
http://d.hatena.ne.jp/hagino_3000/20120320/1332228809





このエントリーをはてなブックマークに追加

Node.jsのモジュールをC++で書く (Hello World編)

C++弱者ながらC++でNodeモジュールを作る事にしたのでいろいろと。

まずは公式サイトの解説を参考にしてみる。

Hello World

最初にHello Worldのコードが載っているが
using namespace v8;
とあるので早速この行を消して、修飾を補完して書くとこうなる。
#include <node.h>
#include <v8.h>

v8::Handle<v8::Value> Method(const v8::Arguments& args) {
    v8::HandleScope scope;
    return scope.Close(v8::String::New("world"));
}

void init(v8::Handle<v8::Object> target) {
    target->Set(v8::String::NewSymbol("hello"),
                v8::FunctionTemplate::New(Method)->GetFunction());
}


NODE_MODULE(hello, init)
v8::Stringやv8::ObjectというクラスがJSのそれぞれの型を表現しており、v8.hで定義されている模様。v8.hのコードを眺めるとJavaScriptの型が全て定義されているのがわかる。

NODE_MODULEマクロがどこで定義されているかを探すと node.h で定義されている。
#define NODE_MODULE(modname, regfunc)                                 \
  extern "C" {                                                        \
    NODE_MODULE_EXPORT node::node_module_struct modname ## _module =  \
    {                                                                 \
      NODE_STANDARD_MODULE_STUFF,                                     \
      regfunc,                                                        \
      NODE_STRINGIFY(modname)                                         \
    };                                                                \
  }
モジュールエクスポート用の関数とモジュールの名前を渡す物らしい。なので上記Hello Worldのコードは、Stringの"world"を返すhelloというメソッドを一つ持つhelloモジュールを作っている。

ビルド


次にモジュールをビルドするためのwscriptのコードが載っている。ビルドするにはwscriptを作ってnode-wafコマンドを使う。

node-wafはpythonのビルドツールのhttp://docs.waf.googlecode.com/git/apidocs_16/index.htmlそのものと書いてある。rubyにおけるrakeコマンドとRakefileみたいな物なのかな。例として載っている

node-waf configure build
configureタスクの後にbuildタスクを実行する。

wscriptの1行目

srcdir = '.'
を適当に変えてソースディレクトリを指すようにしたら、何故かソースが読み込まれない。WAFのドキュメントにもsrcdirは載っていないので、wscriptはソースディレクトリに配置して使うしか無いのかも。

続きます Node.jsのモジュールをC++で書く (引数と戻り値)





このエントリーをはてなブックマークに追加

2012-01-30

エンジニアサポート新年会 CROSS 2012でEmotivについてLTをしました

日本ではあまり使っている人を見ない筋電/脳波ヘッドセットのEmotiv EPOC neuroheadsetを使ってみた。デモはNodejsでデータ飛ばしてCanvasに描画した。視線の位置のxyが取れるのでそれを使った方が面白かったかなぁ。



JavaのコードはSDKのサンプルをベースにJSON作っただけ。状態を取る部分のコードは次の通り。
JSONObject currentState = new JSONObject();
Edk.INSTANCE.EE_EmoEngineEventGetEmoState(eEvent, eState);
float timestamp = EmoState.INSTANCE.ES_GetTimeFromStart(eState);

JSONArray actions = new JSONArray();
if (EmoState.INSTANCE.ES_ExpressivIsBlink(eState) == 1) {
    actions.put("Blink");
}
if (EmoState.INSTANCE.ES_ExpressivIsLeftWink(eState) == 1) {
    actions.put("Left Wink");
}
if (EmoState.INSTANCE.ES_ExpressivIsRightWink(eState) == 1) {
    actions.put("Right Wink");
}
if (EmoState.INSTANCE.ES_ExpressivIsLookingLeft(eState) == 1) {
    actions.put("Looking Left");
}
if (EmoState.INSTANCE.ES_ExpressivIsLookingRight(eState) == 1) {
    actions.put("Looking Right");
}
if (EmoState.INSTANCE.ES_ExpressivIsLookingDown(eState) == 1) {
    actions.put("Looking Down");
}
currentState.put("actions", actions);

currentState.put("Excitement ShortTerm", EmoState.INSTANCE.ES_AffectivGetExcitementShortTermScore(eState));
currentState.put("Excitement LongTerm", EmoState.INSTANCE.ES_AffectivGetExcitementLongTermScore(eState));
currentState.put("Engagement Boredom", EmoState.INSTANCE.ES_AffectivGetEngagementBoredomScore(eState));
currentState.put("Frustration", EmoState.INSTANCE.ES_AffectivGetFrustrationScore(eState));
currentState.put("Meditation", EmoState.INSTANCE.ES_AffectivGetMeditationScore(eState));

send("state", currentState.toString());





このエントリーをはてなブックマークに追加

2012-01-09

キネクハトッカーズマニュアルのサポートページを更新しました。

サポートページ作成時点からかなり状況が変ったので、いろいろと修正しました。OpenNIの進化や、WebSocketでバイナリが使える様になった事など。半年も経てば随分と変る物ですね。


f:id:hagino_3000:20120108233246p:image:w640



このエントリーをはてなブックマークに追加

2012-01-02

OpenNIをopenFrameworksから使う Advanced編

OpenNI Advent Calendarの20日目の記事です。

24日目の記事でもopenFrameworksについて書かれているので、内容がかぶらない様にしています。

何故24日目の記事の方が先に公開されているかは気にしないでください。oFについて基本的な事は上記 @mmlemon_twitter さんの記事に書かれているので割愛します。あと、oFはMacユーザーが多いのでMacOSX + Xcode4の環境を前提に書きます。

ofxOpenNIについて

oFのaddonの一つ。OpenNIをoFっぽいインタフェースで扱える様にした物。誰のブランチを使っていいのかわからないけど、gameoverhack氏のブランチが一番forkされているので自分が使う時はそれを使っている。
DepthGenerator, ImageGenerator, UserGenetator, HandGenerator, IRGenerator, GestureGeneratorの機能はカバーしていて、 exampleを見れば大体の使い方は掴めるはず。ofxOpenNIのジェネレータから得られるデータは、既に世界座標から表示座標系に変換されているため、そのまま表示に使う事ができる。

チルトモーターの首ふりも実装されているが、内部でlibusbを使っているためかMacOSでしか動作しない。Windowsでも首ふりしたかったらOpenNIのXnUSBを使う実装に書きかえれば良い。

リポジトリにある物はKinectで使う事が想定されているらしく、Xtion Proではそのまま使えない。SensorKinectの代りにSensorモジュールを自分でビルドして上書きすればいけそう。あとOpenNIのバージョンも古いので注意。今から使う場合は自分がメンテナになる勢いで使っていかないと厳しいだろう。

アドオンを使わない方法

さて、ofxOpenNIを紹介したが、現在自分の作業マシンにはKinectでは無くXtion Pro liveが接続されているのでofxOpenNIは動かない。なのでoFのプロジェクトにOpenNIを普通に追加して使う時に気にする事を書きます。

config.xmlがアプリケーションの内部に配置される様にする

Build Phasesで「Add Build Phase」してOpenNIの設定ファイルをビルドした.appファイルの中にコピーする。しておかないとHoge.app単体を配布して動作させた時にconfig.xmlが見つからない、とエラーになってしまう。

f:id:hagino_3000:20120102012615p:image:w360

OpenNIの初期化コードは次の通りになる。
xn::EnumerationErrors ctxErrors;
static string configFilePath = ofFilePath::getCurrentWorkingDirectory() + "../Resources/config.xml";
XnStatus rc = openNIContext.InitFromXmlFile(configFilePath.c_str(), &ctxErrors);
if (rc != XN_STATUS_OK) {
  XnChar strError[1024];
  ctxErrors.ToString(strError, 1024);
  throw std::runtime_error(strError);
}

updateメソッドでfpsを落さないようにする

updateメソッドで注意しないといけないのは、WaitAndUpdata系のメソッドを毎回使うと、ofSetFrameRateでフレームレートを指定していてもブロックがかかりfps30程度に落ちてしまう。fpsに影響を与えたく無い場合はジェネレーターのWaitAndUpdateDataを呼ぶ前にIsNewDataAvailableで新しいデータが来ているかチェックする。ofxOpenNIではupdateメソッド内でWaitAndUpdateDataを呼んでいるため、フレームレートが30fpsに落ちてしまう。

コードは次の通り
// 各種ジェネレータとOpenNIコンテキストはヘッダファイルでメンバとして定義してある。
void testApp::update(){
  if (depthGen.IsNewDataAvailable()) {
    // 深度データの更新
    depthGen.WaitAndUpdateData();
  
    xn::DepthMetaData depthMetaData;
    depthGen.GetMetaData(depthMetaData);
  
    XnMapOutputMode mapMode;
    depthGen.GetMapOutputMode(mapMode);
  
    int width = mapMode.nXRes;
    int height = mapMode.nYRes;
    int numPixels = width * height;
  
    float* depthHist = createHist(&depthMetaData, numPixels);
  
    const XnDepthPixel* depthPixels = depthMetaData.Data();
    for (XnUInt i = 0; i < numPixels; i++, ++depthPixels) {
      grayPixels[i] = depthHist[*depthPixels];
    }
  
    grayImage.setFromPixels(grayPixels, width, height); 
  }
  if (imageGen.IsNewDataAvailable()) {
    // ビデオカメラ画像の更新
    imageGen.WaitAndUpdateData();
  
    xn::ImageMetaData imageMetaData;
    imageGen.GetMetaData(imageMetaData);
  
    colorImage.setFromPixels((unsigned char*)imageMetaData.RGB24Data(), colorImage.width, colorImage.height);
  
  }
  if (audioGen.IsNewDataAvailable()) {
    // オーディオの更新
    audioGen.WaitAndUpdateData();
    ofLog(OF_LOG_NOTICE, "Audio data size:" + ofToString(audioGen.GetDataSize()));
  
    // doSomething
  }
}
void testApp::draw(){
    ofScale(0.8, 0.8);
    // 深度画像の描画
    grayImage.draw(0, 0);
    // ビデオカメラ画像の描画
    colorImage.draw(640, 0);
}
どうしてもupdateメソッド内がごちゃごちゃしてくるので、OpenNIの処理を分離するためのクラスを一つ作るのが良いだろう。Audioジェネレーターはこの記事のために初めて使ってみたが、安定して動作しなかったので特に何も処理は実装しなかった。*1

コード一式はgistにアップしてあります。

openFrameworks + Kinectな作品

Vimeoで検索するといろいろ見つかる。

f:id:hagino_3000:20120102011553p:image:w360

自分のおきにいりはWebcam Piano 2.0 Teaserという作品 (Kinectは使っていないけど)

f:id:hagino_3000:20120102011942p:image:w360

まとめ

android対応も進んでおり、openFrameworksも発展も楽しみなプラットフォームです。OpenNIと一緒に使えるようになっておくと良いかもしれません。openFrameworks + Kinectについての解説はキネクハッカーズマニュアルにもページを裂いて書いたので、興味のある方はチェックしてみてください。



書名
キネクトハッカーズマニュアル
著者
@hagino3000 + 小野 憲史
発売日
2011-08-26
価格
2,604 JPY (256ページ)
*1:Windowsでは動作実績があるみたいだが、自分の環境ではすぐにデータが取得できなくなり、Xtion pro liveを抜いて刺し直すというのが必要だった。



このエントリーをはてなブックマークに追加