webRTCで顔認識(face.js)を試してみた。

face.jsって、なんか、顔認識できるらしいぞ。凄い!
そんなわけで、試してみる。

ccv.jsとface.jsを使うと実現できるぽい。
ccv.jsは、コンピュータビジョンのライブラリ。
face.jsは、顔認識するためのパターンデータみたい。

2つのソースは、以下から取得してきた。(どこがオリジナルかわからず。。。)
https://github.com/wesbos/HTML5-Face-Detection

顔を認識っていったら、誰もが一度はやってみたいと思ったはず。
顔を笑い男に上書きするやつを書いてみた。

ソース(sample02.html)

<html>
<head>
<title>sample02</title>
<script type="text/javascript" src="./js/ccv.js"></script>
<script type="text/javascript" src="./js/face.js"></script>
</head>
<body>
<h1>webRTC with face.js</h1>
<script type="text/javascript">
  var src;
  var dest;
  var width;
  var height;

  var image = new Image();
  image.src = "./laughingman.gif";
  
  initialize = function() {
    src = document.getElementById("src");
    dest = document.getElementById("dest");
    navigator.webkitGetUserMedia("video,audio",onGotStream,onFailedStream);
  }
  
  onGotStream = function (stream) {
    var url = webkitURL.createObjectURL(stream);
    src.src = url;
    setInterval(draw,500);
  }
  
  onFailedStream = function(error) {
    alert("onFailedStream");
  }
  
  var draw = function () {
    width = src.videoWidth;
    height = src.videoHeight;
    
    var canvas = document.createElement('canvas');
    
    canvas.width = width;
    canvas.height = height;
    var context = canvas.getContext('2d');
    context.drawImage(src,0,0,width,height);
    
    dest.width = width;
    dest.height = height;
    var context2 = dest.getContext('2d');
    context2.drawImage(src,0,0,width,height);
    
    var comp = ccv.detect_objects({"canvas" :ccv.grayscale(canvas),
      "cascade" : cascade,
      "interval" : 5,
      "min_neighbors" : 1 });
  
    for(var i=0;i < comp.length;i++) {
      context2.drawImage(image,comp[i].x-30,comp[i].y-30,comp[i].width+60,comp[i].height+60);
    }

  }
  
  setTimeout(initialize,1);
  
</script>
<video id="src" autoplay></video>
<canvas id="dest"></canvas>
</body>
</html> 

デモ

動かしてみたいけど面倒くさい人のためにデモページを用意しました。
WebRTCは、Google Chromeの開発版でしか動作しないので、
Chromeの開発版でMediaStreamを有効にして試してください。
あと、Webカメラが必要です。

http://alumican.ddo.jp/webrtc/sample02.html
※動画データは、サーバに送信されませんよ。

動作画面

ちょっとface.jsの認識が微妙だけど、こんな感じです!
なんか1ネタできそう。

f:id:alumican:20120322163301j:plain

WebRTCをシンプルに試す

WebRTCって、なんなの?使うのめんどくさいの?
って、日頃から気になっていたので、簡単に動くことを試してみた。
RTCは、Real-Time Communicationsの略で、ブラウザでカメラとか音とか扱える夢いっぱいの機能です。
前提として、 Google Chromeだと、まだ開発版(Dev channel)しか対応していないらしいので、
開発版をダウンロードして試します。
あと、残念ながらカメラがないと試せません。。。

Chrome開発版ダウンロード と WebRTCの有効化

Chrome開発版をビルドすると大変時間がかかるので、
以下のサイトから環境にあったChrome開発版をダウンロードします。
私が使ったのは、Dev channel for 64-bit Debian/Ubuntuです。
http://dev.chromium.org/getting-involved/dev-channel
ダウンロード後に、Chromeをインストールし、起動します。

URLを入力するアドレス欄に
chrome://flags
を入力すると、試験運用機能の設定画面が表示されるので
「MediaStream を有効にする」をオンにしてください。

その他の準備

Webカメラとapacheか何かWebサーバが必要です。
これで、環境は出来上がりです。

HTMLを書く

以下のカメラ映像を表示するだけのHTMLを書きます。

<html>
<head>
<title>sample01</title>
</head>
<body>
<script type="text/javascript">
var localvideo;
initialize = function() {
  localvideo = document.getElementById("localvideo");
  navigator.webkitGetUserMedia("video,audio",onGotStream,onFailedStream);
}

onGotStream = function(stream) {
  console.log("onGotStream");
  var url = webkitURL.createObjectURL(stream);
  localvideo.src = url;
}

onFailedStream = function(error) {
  alert("onFailedStream");
}

setTimeout(initialize,1000);
</script>
<video id="localvideo" autoplay></video>
</body>
</html>

ChromeでWebサーバにアクセスする

つくったHTMLをそのままChromeにドラッグ&ドロップしても動作しないので、
HTMLをWebサーバで公開し、アクセスします。

f:id:alumican:20120321135604p:plain

Chromeから許可を求められるので、「許可」を押します。
以下のようなカメラ映像が得られれば、成功です!

f:id:alumican:20120321135608p:plain

コンパイラ実装会(3)に参加してきた。その3

前回に引き続き、今回は、brainfuckのコード から アセンブラのコード を生成して出力する node.jsのコードを作成しました。
想定している環境は、Linuxx86_64です。

ネット上のアセンブラの情報がNASMとGASとIntel記法とAT&T記法でごちゃってて、ちょっと一苦労。。。
アセンブラにもNASMとGAS等があり、今回は、GASを。
記法はIntel記法とAT&T記法がありますが、Intel記法です。

bf2asm.js

var fs = require('fs');

var cmds = "";
var indent = 0;
var code = fs.readFileSync('data','utf-8');

var trans = function(code) {
  var arr = code.split('');
  while(arr.length != 0){
    var c = arr.shift();
    switch (c) {
    case '+' : 
      cmds += 'inc byte ptr[esi]\n';
      break;
    case '-' :
      cmds += 'dec byte ptr[esi]\n';
      break;
    case '>' :
      cmds += 'inc esi\n';
      break;
    case '<' :
      cmds += 'dec esi\n';
      break;
    case '.' :
      cmds += 'mov edx, 0x1\n';
      cmds += 'mov ecx, esi\n';
      cmds += 'mov ebx, 0x1\n';
      cmds += 'mov eax, 0x4\n';
      cmds += 'int 0x80\n';
      break;
    //case ',' :
      //cmds += 'ptr = getchar();\n';  
      //break;
    case '[' :
      cmds += indent*2 + ':\n';
      cmds += 'cmp byte ptr [esi],0\n';
      cmds += 'jz ' + (indent*2+1) + 'f\n';
      indent += 1;
      break;
    case ']' :
      indent -= 1;
      cmds += 'jmp ' + indent*2 + "b\n";
      cmds += (indent*2+1) + ':\n';
      break;
    default:
    }
  }
  return cmds;
}

var out = '';
out += '.intel_syntax noprefix\n';
out += '.comm mem, 30000\n';
out += '.code64\n';
out += '.globl _start\n';
out += '_start:\n';
out += 'lea esi, mem\n';
out += trans(code);
out += 'xor ebx,ebx\n';
out += 'mov eax,0x1\n';
out += 'int 0x80\n';

fs.writeFileSync('./bf.s',out,'utf-8');

data (brainfuckのHelloコード)

>+++++++++[<++++++++>-]<.>+++++++[<++++>-]<+.+++++++..+++.[-]>++++++++[<++
++>-]<.>+++++++++++[<+++++>-]<.>++++++++[<+++>-]<.+++.------.--------.[-]>
++++++++[<++++>-]<+.[-]++++++++++.

実行方法

$ node bf2asm.js
$ as bf.s -o bf.o
$ ld bf.o
$ ./a.out 
Hello World!

 

コンパイラ実装会(3)に参加してきた。その2

コンパイラ実装会(3)に参加したら、
「では、@alumicanさん発表お願いします。」(な、なんだってー!)
と、@7shiさんにふられたので発表したネタを公開します。


ELF形式の実行ファイルの中身を解析するツールです。はい。ブラウザで。
コンパイラ実装するにあたり、全然真面目に勉強していなかったので、
「もうELF形式のファイルを直接書き換えちゃえばいいよねw!」
というのが動機ですが、読み込むところまでしかできてません。
以下で公開しています。

http://alumican.ddo.jp/elfreader/

使い方

ELF形式の実行ファイルを「緑の枠」にドラッグアンドドロップします。
うまくいけば、解析されて、
ELF Header
Program Header Table
Segment and Section
Section Header Table
の項目が表示されます。クリックすると中身を解析できます。

うまくいかないと、大体の場合、ブラウザが応答しなくなりますw

解析された内容を下記のサイトを参考にして調べると、
何が入っているのかわかりやすいかなと思います。
http://caspar.hazymoon.jp/OpenBSD/annex/elf.html

※ELF形式のファイルは32bitのみ対応です
ドラッグアンドドロップしたファイルは、ローカルで処理されるのでサーバに送信されたりしません。

コンパイラ実装会(3)に参加してきた。

基本的にハッカソンのように個々人がもくもくと実装する会です。
運がよければ、すごい人の自作VMとか自作JITとか自作言語の発表が聞けちゃいます。
今回は、brainfuckのコード から Cのコード を生成して出力する node.jsのコードを作成しました。
brainfuckは、命令が8つしかないので、処理は簡単です。
以下は、コードと実行方法です。

brainfuck-ctrans.js

var fs = require('fs');

var out = "";
var code = fs.readFileSync('./data','utf-8');
out += '#include \n';
out += 'void main() {\n';
out += 'char m[30000];\n';
out += 'char* ptr = m;\n';

var arr = code.split('');
while(arr.length != 0){
    var c = arr.shift();
    switch (c) {
    case '>' : 
        out += '++ptr;\n';
        break;
    case '<' :
        out += '--ptr;\n';
        break;
    case '+' :
        out += '++(*ptr);\n';
        break;
    case '-' :
        out += '--(*ptr);\n';
        break;
    case '.' :
        out += 'putchar(*ptr);\n';
        break;
    case ',' :
        out += '*ptr = getchar();\n';
        break;
    case '[' :
        out += 'while(*ptr) {\n';
        break;
    case ']' :
        out += '}\n';
        break;
    default:
    }
}
out += '}\n';
fs.writeFileSync('./out.c',out,'utf-8');

data (brainfuckのHelloコード)

>+++++++++[<++++++++>-]<.>+++++++[<++++>-]<+.+++++++..+++.[-]>++++++++[<++
++>-]<.>+++++++++++[<+++++>-]<.>++++++++[<+++>-]<.+++.------.--------.[-]>
++++++++[<++++>-]<+.[-]++++++++++.

実行方法

  1. node brainfuck-ctrans.js
  2. gcc out.c 
  3. a.out

Hello World!
と、表示されます。(たぶん)

次は

次は、brainfuckのコード から アセンブラのコード を生成して出力する node.jsのコードを
作成します。

Androidデバイス間でP2P通信できるAllJoynをためしてみた。

横浜Androidプラットフォーム部第18回勉強会でAndroid Builders Summitの紹介があったときに はじめてAllJoynのプロジェクトを知ったので、簡単にサンプルを試してみました。
まだ、Androidデバイス間のP2P通信ができるおもしろいところまでいっていないですが、
次回ということで。ひとつ。

AllJoynとは

Qualcommが作ってるDbusをデバイス間にも拡張したP2Pシステムらしい。
オープンソースなのでGitHubにて公開されています。
froyoとgingerbreadの他、ICSでも動作します。

プロジェクトのサイトは以下
https://www.alljoyn.org/

前提要件

Android SDK 
Android NDK
Eclipse 
Eclipse ADT plug-in

その他の必要なパッケージ

aptitude install scons

Android SDKをインストール

下記のサイトを参考にしてください。
http://developer.android.com/intl/ja/sdk/index.html
Eclipse と ADT plug-in についても記述されているとおもうので省略します。

Android NDKをインストール

# mkdir -p $HOME/alljoyn
# cd $HOME/alljoyn
# wget http://dl.google.com/android/ndk/android-ndk-r7b-linux-x86.tar.bz2
# tar jxvf android-ndk-r7b-linux-x86.tar.bz2

詳細は下記のサイトを参考にしてください。
http://developer.android.com/intl/ja/sdk/ndk/index.html

Androidのソース取得とビルド

# mkdir -p $HOME/alljoyn/android-src
# cd $HOME/alljoyn/android-src

$HOME/alljoyn/android-srcの中にソースを取得して、ビルドしてください。
詳細は下記のサイトを参考にしてください。(ビルドにすごい時間かかります・・・)
http://source.android.com/source/downloading.html

alljoynのソース取得とビルド

$ mkdir -p $HOME/alljoyn/repo
$ cd $HOME/alljoyn/repo
$ repo init -u git://github.com/alljoyn/manifest.git
$ repo sync
$ repo start master --all

$ mkdir -p $HOME/alljoyn/lib
$ cd $HOME/alljoyn/lib
$ wget https://github.com/downloads/KentBeck/junit/junit-4.10.jar
$ jar xvf junit-4.10.jar
$ export JAVA_HOME=/opt/jdk1.6.0_30/ <-環境によって変更してください。
$ export CLASSPATH=$HOME/alljoyn/lib
$ cd $HOME/alljoyn/repo
$ scons OS=android CPU=arm ANDROID_NDK=$HOME/alljoyn/android-ndk-r7b ANDROID_SRC=$HOME/alljoyn/android-src WS=off

libcrypto.soを取得

Androidエミュレータを起動する。(※avdの設定hogehogeは作成済みという前提)
$ emulator -avd hogehoge
$ cd $HOME/alljoyn/repo/build/android/arm/debug/dist/lib
$ adb pull /system/lib/libcrypto.so libcrypto.so

なぜか、ターゲットになるAndroidのlibcrypto.soを取得するという手順。
それって、どうなの?

AllJoynデーモンの作成とインストール

$ cd $HOME/alljoyn/repo/build/android/arm/debug/dist/alljoyn_android/alljoyn/
$ $HOME/alljoyn/android-ndk-r7b/ndk-build

eclipseを起動

新しいプロジェクトを作成
File -> New -> project -> Android Project
[Create project from existing source]を選択
location に [$HOME/alljoyn/repo/build/android/arm/debug/dist/alljoyn_android/alljoyn]を指定
finishを選択

ビルド
Project -> Build Project

エクスポート
File -> Export... -> Export Android Application から alljoyn.apk を作成

AllJoynデーモンプログラムをインストールする。
$ adb install alljoyn.apk

AllJoynデーモンプログラムを実行すると以下のようにバックグラウンドで常駐します。
f:id:alumican:20120229093140p:plain

サンプルの作成とインストール

alljoynのサンプルで動作検証する。

Simple Clientアプリ

eclipseを起動

新しいプロジェクトを作成
File -> New -> project -> Android Project
[Create project from existing source]を選択
location に [$HOME/alljoyn/repo/build/android/arm/debug/dist/java/samples/simple/client]を指定
finishを選択

ビルド
Project -> Build Project

エクスポート
File -> Export... -> Export Android Application から Client.apk を作成

Simple Serviceアプリ

eclipseを起動

新しいプロジェクトを作成
File -> New -> project -> Android Project
[Create project from existing source]を選択
location に [$HOME/alljoyn/repo/build/android/arm/debug/dist/java/samples/simple/service]を指定
finishを選択

ビルド
Project -> Build Project

エクスポート
File -> Export... -> Export Android Application から Service.apk を作成

サンプルアプリをインストールする。

$ adb install Client.apk
$ adb install Service.apk

検証方法

以下の順番で実行する。
AllJoynデーモン(alljoyn.apk)
Simple Service(Service.apk) ※必ずHOMEボタンでぬけること。BACKボタンで抜けるとうまく動作しません。
Simple Client(Client.apk)

クライアントアプリ(Client.apk)のアクティビティの「Enter Message」に文字を入力し、
Enterキーを押すと、Simple Serviceが応答し、検証できます。

以下が成功した画面です。

f:id:alumican:20120301120400p:plain

東京Node学園 4時限目に参加してきました。

東京Node学園 4時限目に参加してきました。
内容は、最初にnode.jsでWebSocketを使うまでの説明があって、
その後は、主にハッカソン。ひとりでモクモク実装する感じでした。

使われてた資料

 

Socket.IOを用いたマウスカーソルの共有について

え?socket.ioでマウスカーソルの共有とかできんの!?まじかー!
「サーバには頻繁にアクセスして負荷かけちゃいけない」という既成概念に囚われていたため衝撃をうけました。
実際動かしてみたくなったので、コードを一部丸パクリしつつ動かします。

expressのテンプレートを生成

# express -t ejs -s sample

packageにsocket.ioを追加

# sample/package.json
{
    "name": "application-name"
  , "version": "0.0.1"
  , "private": true
  , "dependencies": {
      "express": "2.5.5"
    , "ejs": ">= 0.0.1"
    , "socket.io": "*"   ←追記
  }
}

npmで必要なモジュールをインストール

# npm install

sample/app.js に追記

var io = require('socket.io').listen(app);
io.sockets.on('connection', function(socket) {
  socket.on('mousemove',function (data) {
    var d = {};
    d.id = socket.id;
    d.x = data.x;
    d.y = data.y;
    socket.broadcast.emit('mousemovecb',JSON.stringify(d));
  });
});

sample/views/layout.ejs に追記

<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
<script type="text/javascript" src="/socket.io/socket.io.js"></script>

sample/views/index.ejs に追記

<script type="text/javascript">
var socket = io.connect();
socket.on('mousemovecb', function(data) {
  var data = JSON.parse(data);
  var cursor = $('#' + data.id);
  if(!cursor.attr('id')) {
    cursor = $('<img>');
    cursor.attr('src', 'images/cursor.png');
    cursor.css('position','absolute');
    cursor.css('width','18px');
    cursor.css('height','24px');
    $('#wrapper').append(cursor);
  }
  console.log(data);
  cursor.css('left', data.x + 'px');
  cursor.css('top', data.y + 'px');
  cursor.show();
  setTimeout(function () {
    cursor.hide();
  }  ,1000);
});

$(function() {
  $('#wrapper').mousemove(function(e) {
    socket.emit('mousemove', {
      x: e.pageX,
      y: e.pageY
    });
  });
});
</script>
<div id="wrapper" style="height: 300px; width: 300px; background: #ddd;"></div>

以下になんか適当なPNGとかJPGとかのアイコンを配置する。

sample/public/images/cursor.png

起動

# node app.js

以下のURLを2つのブラウザで開く

http://localhost:3000/

片方のブラウザで表示された灰色のエリア上でマウスを動かすと、
もう片方のブラウザでcursor.pngアイコンが表示される。
このサイトをグローバルに立てれば、みんなでマウスを共有できる。 これは燃えるな~。

ハッカソンの成果発表

カメラキャプチャした動画をnodeでリアルタイムに画像処理して配信する発表や
iPhoneでWebSocketでセッションをはったブラウザに音を鳴らす発表など
おぉ!なるほど!と思わせる内容が盛りだくさん。

個人的には、PandaBoardにnodeを載せてもいい感じという話が
目から鱗でした。(サーバ以外にnode入れるんだ!みたいな)
良い刺激を受けた一日になりました。
次こそは成果物をあげたい。