リアルタイム顔認識


Androidでリアルタイムな顔認識ができるか試してみた。IS06で試してみたら2.4fpsぐらい。動くかわからんけど一応置いときます。https://github.com/downloads/hogelog/android-test/FaceDetect.apk
ソースコードgithubhttps://github.com/hogelog/android-test/tree/master/facedetect


顔認識自体はandroid.media.FaceDetectorが最初から組み込まれているのでかなり簡単。カメラの映像をリアルタイムに処理するにはCamera.PreviewCallback.onPreviewFrame(byte, android.hardware.Camera)を使うわけですが、このbyte列がYUV420なる形式にしかならない*1ので自前で変換。ワーキンググループ/金沢支部/第四回勉強会 - 日本Androidの会(日本アンドロイドの会)を参考に。

*1:setPreviewFormatを使ってもRGB形式などにはならない

録音と再生

AndroidManifest.xml

    <uses-permission android:name="android.permission.RECORD_AUDIO" />

を忘れずに。あとはhttp://developer.android.com/reference/android/media/MediaRecorder.htmlに書いてる通りに

 MediaRecorder recorder = new MediaRecorder();
 recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
 recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
 recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
 recorder.setOutputFile("/sdcard/hoge.3gp");
 recorder.prepare();
 recorder.start();   // Recording is now started
 ...
 recorder.stop();

といった流れで録音。再生は以下のように。

 MediaPlayer player = new MediaPlayer();
 player.setDataSource("/sdcard/hoge.3gp");
 player.prepare();
 player.start();
...
 player.stop();

録音と再生をするだけの簡単なアプリを書いてみた。簡単で良い。
https://github.com/hogelog/android-test/tree/master/record_and_play

Wi-FiスキャンしたSSIDの一覧を表示

WifiManager.getScanResults叩いて最後にスキャンした結果の取得。スキャンをさせたいならstartScan。
WifiManager  |  Android Developers
ListViewでSSID一覧を表示させてみた。

package org.hogel.android.wifiscan;

import java.util.List;

import android.app.ListActivity;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiManager;
import android.os.Bundle;
import android.widget.ArrayAdapter;

public class WifiScanActivity extends ListActivity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        final WifiManager manager = (WifiManager) getSystemService(WIFI_SERVICE);
        if (manager.getWifiState() == WifiManager.WIFI_STATE_ENABLED) {
        	List<ScanResult> results = manager.getScanResults();
        	final String[] items = new String[results.size()];
        	for (int i=0;i<results.size();++i) {
        		items[i] = results.get(i).SSID;
        	}
        	final ArrayAdapter<String> adapter = 
        		new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, items);
        	setListAdapter(adapter);
        }
    }
}

android.permission.ACCESS_WIFI_STATEのパーミッションが必要。
githubにプロジェクト置いといてるhttps://github.com/hogelog/android-test/tree/master/wifiscan

携帯変えました

はじめに

いいですね。便利です。Android 2.2ということで色々サクサク動きます。バッテリが弱いのはちょっと困りますがポータブルUSB充電器などなどでどうとでもなる。あとはマーケットの不具合も少し困りますけどそれもまあどうとでもなりますね。

だいたいの情報はトップページ - SIRIUSα IS06 まとめ wiki - アットウィキ見れば大丈夫です。

入れたアプリ

Advanced Task Killer Froyo
たまに手動でアプリを殺す。自動で復活してくるようなアプリはIgnoreに入れて殺さない。自動殺し機能は使わない
Adobe Flash Player 10.1
Android 2.2の強みの一つ。ニコニコ動画の動作もとりあえずは確認。IS06の不具合(?)で映像と音がずれるときがあるようですが。
twicca
たぶん定番Twitterクライアント
Facebook
最近Twitterよりもfacebookのが好みです。
MSN Droid
Windows Liveメッセンジャ代替ソフトウェア。MSN Talkというものもありますがこっちのがかっこいい。
Quick Settings
色々簡単に設定できて良い。
Adobe Reader
まださほど使ってないけど。
PreHome
PreHomeでアプリケーションを切り替えてるとホーム画面なんてもんあんまいらねーんだなーという感じで面白い。
LauncherPro
まあカスタマイズできていいんじゃないでしょうか。
Chrome to Phone
Google ChromeからURLを送ってIS06で開いたり、テキストをIS06側にコピーさせるなどできてかなり便利。
Battery Mix
バッテリ残量を表示するためだけに。監視機能はオフ。そのうちバッテリ食うアプリを検証する時にオンにする。
QRコードスキャナー
QRコード読みます。
MyQR
自分の連絡先をQRコードで表示できるように。他の人に読んでもらえば赤外線いらずで交換できる。
ConnectBot
SSHクライアント。ハードウェアキーボードが無いからあまり使わないかも。ローカルで動くシェルにも。
ロケタッチ
かわいくていいかなーと。
SugarSync
DropboxよりもSugarSyncの方が好き。https://www.sugarsync.com/referral?rf=fer68e63dx89jから始めてくれると500MBの追加容量があなたと私に!
SL4A
Scripting Layer for AndroidPython, JRuby, Lua, shなどのスクリプト言語インタプリタが使えるようになります。
LDRMate
fastladderを使ってるのでこっち。LDRならLDRoidもいいのかもしれない。
anちゃん
2chのIS06スレを確認するためだけにインストール。なので自動監視などの機能はオフ。
ES ファイルエクスプローラ
ファイラ。

USBケーブルで充電

IS06に付属してくるmicroUSB<->USBケーブルはPCと接続して充電ができない。
http://d.hatena.ne.jp/ramencozo/20101226/1293380316

実は付属ケーブルもD±端子触れないように中途半端に挿すと充電できたりする。けどまあどうなんだろうなと思うので充電・通信切り替えできるケーブル購入。

リックス iCharger XPERIA (SO-01B) 用 リール式USB通信・充電器 (microUSB) 充電・通信切替スイッチ付き(ブラック)

リックス iCharger XPERIA (SO-01B) 用 リール式USB通信・充電器 (microUSB) 充電・通信切替スイッチ付き(ブラック)

ちなみに対応機種というシールに「Sirius α」と書いてあった切り替えなしの充電・通信可能ケーブルは充電できないことを確認して返品。IS06とかあんまし売れてないので店員さんやらなんやらの言ってることはさほど信用できない。

あとPCとIS06とあれこれ通信するためにはWindowsだとパンテックが提供してるドライバを入れる必要があります。Mac OS X 10.6.4だと繋ぐだけでちゃんとandroid-sdkと通信できました。

おわりに

そんなわけでMacBook Air 11インチ欲しい!

Lua処理系コード読み(13) luaL_newstate

luaL_newstateは先のlua_newstateにLuaが提供するシンプルなアロケータ(とエラー発生時呼び出す関数)を指定して呼び出す関数。

LUALIB_API lua_State *luaL_newstate (void) {
  lua_State *L = lua_newstate(l_alloc, NULL);
  if (L) lua_atpanic(L, &panic);
  return L;
}

l_allocの中身はだいぶ謎。nsizeが0のときreallocはfreeと等価なはずだし意味がないのではと思うけど、なんかそうなってない環境があるのかなー。

static void *l_alloc (void *ud, void *ptr, size_t osize, size_t nsize) {
  (void)ud;
  (void)osize;
  if (nsize == 0) {
    free(ptr);
    return NULL;
  }
  else
    return realloc(ptr, nsize);
}

ud, osizeという引数はこの関数ではもちろん必要ないけど、自作アロケータを使うときに使う。udにそのアロケータが関連付けてないといけない領域が与えられるのでそんな感じに扱いましょう的なそんな。(void)ud;(void)osize;はたぶん警告とかエラーを潰す何かかなー。たいていの環境だと意味はそんなない気がする。

LUA_API lua_Alloc lua_getallocf (lua_State *L, void **ud) {
  lua_Alloc f;
  lua_lock(L);
  if (ud) *ud = G(L)->ud;
  f = G(L)->frealloc;
  lua_unlock(L);
  return f;
}


LUA_API void lua_setallocf (lua_State *L, lua_Alloc f, void *ud) {
  lua_lock(L);
  G(L)->ud = ud;
  G(L)->frealloc = f;
  lua_unlock(L);
}

lua_getallocf, lua_setallocf経由でアロケータをセットしたり。

Lua処理系コード読み(12) lua_newstate

lua_newstateは一個のlua_Stateとglobal_Stateからなるメインスレッド(ネイティブスレッドではなくいわゆるグリーンスレッド)を初期化する関数。基本的には領域とってきて値を設定してるだけです。

LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) {
  int i;
  lua_State *L;
  global_State *g;
  void *l = (*f)(ud, NULL, 0, state_size(LG));
  if (l == NULL) return NULL;
  L = tostate(l);
  g = &((LG *)L)->g;
  L->next = NULL;
  L->tt = LUA_TTHREAD;
  g->currentwhite = bit2mask(WHITE0BIT, FIXEDBIT);
  L->marked = luaC_white(g);
  set2bits(L->marked, FIXEDBIT, SFIXEDBIT);
  preinit_state(L, g);
  g->frealloc = f;
  g->ud = ud;
  g->mainthread = L;
  g->uvhead.u.l.prev = &g->uvhead;
  g->uvhead.u.l.next = &g->uvhead;
  g->GCthreshold = 0;  /* mark it as unfinished state */
  g->strt.size = 0;
  g->strt.nuse = 0;
  g->strt.hash = NULL;
  setnilvalue(registry(L));
  luaZ_initbuffer(L, &g->buff);
  g->panic = NULL;
  g->gcstate = GCSpause;
  g->rootgc = obj2gco(L);
  g->sweepstrgc = 0;
  g->sweepgc = &g->rootgc;
  g->gray = NULL;
  g->grayagain = NULL;
  g->weak = NULL;
  g->tmudata = NULL;
  g->totalbytes = sizeof(LG);
  g->gcpause = LUAI_GCPAUSE;
  g->gcstepmul = LUAI_GCMUL;
  g->gcdept = 0;
  for (i=0; i<NUM_TAGS; i++) g->mt[i] = NULL;
  if (luaD_rawrunprotected(L, f_luaopen, NULL) != 0) {
    /* memory allocation error: free partial state */
    close_state(L);
    L = NULL;
  }
  else
    luai_userstateopen(L);
  return L;
}

メモリロケーション命令のエラーが発生しかねない初期化に関してはf_luaopen関数の中にまとめてluaD_rawrununprotected関数経由で呼ぶ。luaD_rawrununprotected内部にはtry/catch(のようなもの)があり、f_luaopen内部からthrowが飛んでくるとエラーを返す。f_luaopenが正常に実行できていれば0を返す。つまり

  else
    luai_userstateopen(L);

はf_luaopenがエラー(メモリが足りないとかアロケータが腐ってるとかそういう状況。つまり普通エラーにならない)じゃなければ呼ばれる。

LG, LUAI_EXTRASPACE, state_size(LG), tostate(l), fromstate(l)

typedef struct LG {
  lua_State l;
  global_State g;
} LG;

LGはまあ見たまんまの。

/*
@@ LUAI_EXTRASPACE allows you to add user-specific data in a lua_State
@* (the data goes just *before* the lua_State pointer).
** CHANGE (define) this if you really need that. This value must be
** a multiple of the maximum alignment required for your machine.
*/
#define LUAI_EXTRASPACE		0

#define state_size(x)	(sizeof(x) + LUAI_EXTRASPACE)
#define fromstate(l)	(cast(lu_byte *, (l)) - LUAI_EXTRASPACE)
#define tostate(l)   (cast(lua_State *, cast(lu_byte *, l) + LUAI_EXTRASPACE))

state_size(LG)=state_size(L)+sizeof(global_State)=sizeof(L)+LUAI_EXTRASPACE+sizeof(global_State)。スレッド領域を確保するときにLUAI_EXTRASPACE分も確保してくれて、fromstate(L)とかすることでその領域のアドレスを得ることができる。スレッド毎に保持しておきたいデータがあればここをいじる。LuaさんはCアプリに組み込んで使うことが多いのでこういうことができるように。

/*
@@ luai_userstate* allow user-specific actions on threads.
** CHANGE them if you defined LUAI_EXTRASPACE and need to do something
** extra when a thread is created/deleted/resumed/yielded.
*/
#define luai_userstateopen(L)		((void)L)
#define luai_userstateclose(L)		((void)L)
#define luai_userstatethread(L,L1)	((void)L)
#define luai_userstatefree(L)		((void)L)
#define luai_userstateresume(L,n)	((void)L)
#define luai_userstateyield(L,n)	((void)L)

メインスレッド初期化のときにluai_userstateopen、他のスレッド初期化時にluastatethreadなど適宣呼んでくれるので初期化したい構造のときはこの名前でdefineしてくださいねということ。luaconf.hを参考に。

Lua処理系コード読み(11) global_State

Lua VM一個にglobal_Stateが一個。VMが保持しておくべきアロケータへのポインタとかGCの状態とかを保持しておく。全てのlua_Stateはこのglobal_Stateへの参照を保持する。

/*
** `global state', shared by all threads of this state
*/
typedef struct global_State {
  stringtable strt;  /* hash table for strings */
  lua_Alloc frealloc;  /* function to reallocate memory */
  void *ud;         /* auxiliary data to `frealloc' */
  lu_byte currentwhite;
  lu_byte gcstate;  /* state of garbage collector */
  int sweepstrgc;  /* position of sweep in `strt' */
  GCObject *rootgc;  /* list of all collectable objects */
  GCObject **sweepgc;  /* position of sweep in `rootgc' */
  GCObject *gray;  /* list of gray objects */
  GCObject *grayagain;  /* list of objects to be traversed atomically */
  GCObject *weak;  /* list of weak tables (to be cleared) */
  GCObject *tmudata;  /* last element of list of userdata to be GC */
  Mbuffer buff;  /* temporary buffer for string concatentation */
  lu_mem GCthreshold;
  lu_mem totalbytes;  /* number of bytes currently allocated */
  lu_mem estimate;  /* an estimate of number of bytes actually in use */
  lu_mem gcdept;  /* how much GC is `behind schedule' */
  int gcpause;  /* size of pause between successive GCs */
  int gcstepmul;  /* GC `granularity' */
  lua_CFunction panic;  /* to be called in unprotected errors */
  TValue l_registry;
  struct lua_State *mainthread;
  UpVal uvhead;  /* head of double-linked list of all open upvalues */
  struct Table *mt[NUM_TAGS];  /* metatables for basic types */
  TString *tmname[TM_N];  /* array with tag-method names */
} global_State;

test