buzztter.comでtwitter上の最近の頻出単語の統計を出してくれている。
頻出語の上位5単語で検索してタイムラインを作るYahoo Pipesを作った。
→ Pipes: buzztter timeline
1週間ぐらい見ているが、だいたいみんなテレビとかゲームとか選挙とか芸能人の話題、眠気や月曜日出勤したくないなどの体調の問題を喋っている。ワイドショー的。
上位3語や10語も試したけど、5語ぐらいが話題がバラけすぎもせず偏りすぎもせずちょうどよかった。
前作「twitter上で親しい人のタイムラインを動的に生成するYahoo Pipes」は自分に近い人がでてくるけど、今度のはtwitterの中心近くで騒いでいる人たちが見える。
プレステ3のカメラをWindowsのVisualStudio2008環境のOpenCVで使う方法をまとめる。
ゲーム用の画像解析用のカメラなので
- 解像度は640×480もしくは320×240と高くは無いが、320×240の時は最大120FPSでキャプチャできる
- レンズのモードを切り替える事が出来て、かなり広角になる
- オートフォーカスが無いので画像処理しやすい
- ゲインやホワイトバランスの調整が高速
- 4000円前後で手に入る。性能の割に安い
この記事で使ったプログラムは全部OpenCV勉強リポジトリにアップロードした。
売り上げランキング: 1418

GAMEはあんまり・・・
まぁ、及第点
確実に使用できるWebカメラ
気になる点を
お得!WindowsXP, VISTAでも問題なく使用できましたよ!■準備
詳しくはVS2008 OpenCVでPlaystation3 eyeを使うにまとめた。
Macではmacamを通せば普通のwebcamとして使えるし、LinuxでもgpscaにOV534というPS3Eyeが使っているチップのドライバを入れれば動くらしい
Windowsの場合、Alexさんという方が作ったドライバを入れれば使える。ただし普通のwebcamとして認識は出来ないのでFlashなどからは使えない。
ドライバと一緒にIPS3EyeLib.hが付いてきて、これを通すとDirectShowのFilterとして使えるようになる。
Visual Studio2008のC++でOpenCVを使いたいので、これもプロジェクトに読み込むようにした。
Visual Studio2008でのOpenCVのセットアップ自体はVC++2008にOpenCV環境セットアップ、カメラでキャプチャに書いた。
■キャプチャしてみる
IPS3EyeLib.hなどへのパスを通したら、プログラムを書いてみる。
dandelion’s log ? PS3EyeLibで遊んでみたに書いてあるコードが参考になった。
作ったのはこれ → PlayStation3 eyeでキャプチャして表示
このプログラムは、普通のwebcamでキャプチャして表示するだけプログラムのカメラ取得と画像取得データ部分をPS3Eye用に置き換えたもの。比較するとわかりやすいと思う。
違いは、カメラを取得するのが
cvCreateCameraCapture(0);の代わりに
IPS3EyeLib::Create();でカメラを取得するのと、
image = cvQueryFrame(capture);で画像を取ってこれる所が
PBYTE capBuffer = new BYTE[(WIDTH * HEIGHT * DEPTH) / 8];という風に一度バッファに渡さないとならない。面倒。
capture->GetFrame(capBuffer, DEPTH, false)
memcpy(image->imageData, capBuffer, image->imageSize);
■画像処理してみる
Teleshadowでも使っている、cvRunningAvgを使って背景統計を取り背景差分するのコードをPS3Eyeで動かしてみる。
背景差分して影を作る。写真の通り、かなり近づいてカメラの外側にいるのに画面にちゃんと体が写っていることから広角っぷりがわかる。

コードはOpenCV Study: dcd37cb7a048 BgSubAvgPs3eye/bgsubAvgPs3eye.cppにアップロードした。
PS3Eyeはゲインやホワイトバランスの調整やオートゲイン調整のon/offができるんだけど、一度オートをoffにしてプログラムを終了させてから再度起動すると前にoffにした設定が引き継がれてしまって真っ暗になる。起動時に全部onにしなければならない。
今回は最初だけオートにしつつゲインだけ固定して、安定したら全部オートをoffにするようにした。これが一番安定して影が作れる。
bgsubAvgPs3eye.cpp
#include <stdio.h>
#include <highgui.h>
#include <cv.h>
#include <highgui.h>
#include <ctype.h>
#include <iostream>
#include <cstdlib>
#include <IPS3EyeLib.h>
#define WIDTH 320
#define HEIGHT 240
#define FPS 30
#define DEPTH 24
#define INIT_TIME 200
#define BG_RATIO 0.02 // 背景領域更新レート
#define OBJ_RATIO 0.005 // 物体領域更新レート
#define Zeta 10.0
int main(int argc, char** argv)
{
bool isStop = false;
IplImage *img = cvCreateImage(cvSize(WIDTH, HEIGHT), IPL_DEPTH_8U, 3);
IPS3EyeLib *capture = IPS3EyeLib::Create();
capture->AutoAGC(false); // ゲイン固定
capture->SetGain(1);
capture->AutoAWB(true);
capture->AutoAEC(true);
if(capture == NULL){
printf("capture device not found!");
return -1;
}
IplImage *imgAverage = cvCreateImage(cvSize(WIDTH, HEIGHT), IPL_DEPTH_32F, 3);
IplImage *imgSgm = cvCreateImage(cvSize(WIDTH, HEIGHT), IPL_DEPTH_32F, 3);
IplImage *imgTmp = cvCreateImage(cvSize(WIDTH, HEIGHT), IPL_DEPTH_32F, 3);
IplImage *img_lower = cvCreateImage(cvSize(WIDTH, HEIGHT), IPL_DEPTH_32F, 3);
IplImage *img_upper = cvCreateImage(cvSize(WIDTH, HEIGHT), IPL_DEPTH_32F, 3);
IplImage *imgSilhouette = cvCreateImage(cvSize(WIDTH, HEIGHT), IPL_DEPTH_8U, 1);
IplImage *imgSilhouetteInv = cvCreateImage(cvSize(WIDTH, HEIGHT), IPL_DEPTH_8U, 1);
IplImage *imgResult = cvCreateImage(cvSize(WIDTH, HEIGHT), IPL_DEPTH_8U, 3);
capture->SetFormat(IPS3EyeLib::GetFormatIndex(WIDTH, HEIGHT, FPS));
PBYTE capBuffer = new BYTE[(WIDTH * HEIGHT * DEPTH) / 8];
capture->StartCapture();
printf("背景初期化中...¥n");
cvSetZero(imgAverage);
for(int i = 0; i < INIT_TIME; i++){
if(capture->GetFrame(capBuffer, DEPTH, false)){
memcpy(img->imageData, capBuffer, img->imageSize);
}
cvAcc(img, imgAverage);
printf("輝度平均 %d/%d¥n", i, INIT_TIME);
}
cvConvertScale(imgAverage, imgAverage, 1.0 / INIT_TIME);
cvSetZero(imgSgm);
for(int i = 0; i < INIT_TIME; i++){
if(capture->GetFrame(capBuffer, DEPTH, false)){
memcpy(img->imageData, capBuffer, img->imageSize);
}
cvConvert(img, imgTmp);
cvSub(imgTmp, imgAverage, imgTmp);
cvPow(imgTmp, imgTmp, 2.0);
cvConvertScale(imgTmp, imgTmp, 2.0);
cvPow(imgTmp, imgTmp, 0.5);
cvAcc(imgTmp, imgSgm);
printf("輝度振幅 %d/%d¥n", i, INIT_TIME);
}
cvConvertScale(imgSgm, imgSgm, 1.0 / INIT_TIME);
printf("背景初期化完了¥n");
capture->AutoAWB(false); // ホワイトバランス固定
capture->AutoAEC(false);
char winNameCapture[] = "Capture";
char winNameSilhouette[] = "Silhouette";
cvNamedWindow(winNameCapture, 0);
cvNamedWindow(winNameSilhouette, 0);
int waitKey = 0;
while(1){
if(!isStop){
if(capture->GetFrame(capBuffer, DEPTH, false)){
memcpy(img->imageData, capBuffer, img->imageSize);
}
if(img == NULL) break;
cvConvert(img, imgTmp);
// 輝度範囲
cvSub(imgAverage, imgSgm, img_lower);
cvSubS(img_lower, cvScalarAll(Zeta), img_lower);
cvAdd(imgAverage, imgSgm, img_upper);
cvAddS(img_upper, cvScalarAll(Zeta), img_upper);
cvInRange(imgTmp, img_lower, img_upper, imgSilhouette);
// 輝度振幅
cvSub(imgTmp, imgAverage, imgTmp);
cvPow(imgTmp, imgTmp, 2.0);
cvConvertScale(imgTmp, imgTmp, 2.0);
cvPow(imgTmp, imgTmp, 0.5);
// 背景領域を更新
cvRunningAvg(img, imgAverage, BG_RATIO, imgSilhouette);
cvRunningAvg(imgTmp, imgSgm, BG_RATIO, imgSilhouette);
// 物体領域を更新
cvNot(imgSilhouette, imgSilhouetteInv);
cvRunningAvg(imgTmp, imgSgm, OBJ_RATIO, imgSilhouetteInv);
cvErode(imgSilhouette, imgSilhouette, NULL, 1); // 収縮
cvDilate(imgSilhouette, imgSilhouette, NULL, 2); // 膨張
cvErode(imgSilhouette, imgSilhouette, NULL, 1); // 収縮
cvMerge(imgSilhouette, imgSilhouette, imgSilhouette, NULL, imgResult);
cvShowImage(winNameCapture, img);
cvShowImage(winNameSilhouette, imgResult);
}
waitKey = cvWaitKey(33);
if(waitKey == 'q') break;
if(waitKey == ' '){
isStop = !isStop;
if(isStop) printf("stop¥n");
else printf("start¥n");
}
}
delete [] capBuffer;
capture->StopCapture();
cvDestroyWindow(winNameCapture);
cvDestroyWindow(winNameSilhouette);
return 0;
}
今回は320×240で30FPSで動かしたが、
dandelion’s log PS3EyeLibで遊んでみたより
320×240@15fps
320×240@30fps
320×240@60fps
320×240@75fps
320×240@100fps
320×240@125fps
640×480@15fps
640×480@30fps
640×480@40fps
640×480@50fps
640×480@60fps
640×480@75fps
で動くらしい。
起動時にこんなエラーが出た。環境はMacbook pro 17インチ OSX LeopardでXCodeなどもたぶん全部入れてあるメイン開発環境。
java.lang.UnsatisfiedLinkError: /Applications/arduino/Arduino.app/Contents/Resources/Java/librxtxSerial.jnilib: no suitable image found.

原因は64bit環境で実行されようとしているかららしい。
対処法は2つ。
■Arduinoを32bit版で起動させる
Arduino実行ファイルを右クリックして「情報を見る」
「32ビットモードで開く」にチェック入れる
(hitoriblogさんより)
■64bit版rxtxSerial.jnilibをインストールする
http://iharder.sourceforge.net/current/java/librxtxSerial.jnilib を
/Arduino 16.app/Contents/Resources/Java/
の中にある librxtxSerial.jnilib と入れ替える。
これはTechnobabble: Arduino: Problems With librxtxSerial.jnilibに書いてあった方法。
両方ともIDEの起動までは試した。
Arduinoの実機が今ないので動作は試していない。まあどっちかで動くだろう。
他にもフォーラムでも同様の質問が出ていて、こちらでは
アプリケーション/ユーティリティ/Java Preferences
でJavaアプリケーションの方でJ2SE5.0の32bit版が優先されるように上の方にもってこいという指示が出ていた。質問者はこれで解決したらしいけど俺の環境では駄目だった。
これはArduinoがJ2SE6向けに作られていないため。
修論を出した後のテンションでまたYahooPipesを作った。
→ Pipes: twitter dynamic timeline
親しい人で動的にタイムラインを作るpipes。
しくみは単純で、最近favを付けたり付けられたり、replyしたりされたりした相手でタイムラインを作る。
自分の発言内容やそれに対する相手の反応によってメンバーが動的に入れ替わる。
これの面白いのは、キーワードでの振り分けと異なりごく普通にtwitterを使っているだけでタイムラインが親しいメンツに変わっていく。
一見流動性が無いように見えるが、自分をふぁぼったりreplyしてきた人は入ってくるし、その人にreplyしたりfavし返せばしばらくはdynamic timelineに入り続ける。相手をfollowする必要すらない。
忙しくてtwitter見れない時は、これだけ見ておけば親しい人のpostはだいたいチェックできるかもしれない。
RSSにはusericons.relucks.orgを使ってアイコンを表示した。ユーザ名やURLもリンクになるようにしてある。

pipes全体図

一番上ですぐ分岐させて、左から順に
- favをもらった相手:favotterのユーザページからHTMLスクレイピングしてる
- 最近replyを受けた相手:オフィシャルtwitter検索でユーザ名検索してatom feedから取得
- favをつけた相手:ユーザのfavページからatom feedで取得
- 最近自分がreplyした人:自分のpostからatom feedで取得
YahooPipesは処理時間が長くなりすぎるか、pipesから相手へのアクセスが多くなりすぎるとエラーが出てしまう。
処理時間が長くならないようにするには並列化。pipeを縦につなげないようにする。
重要なのはpipesから外部サイトへのHTTPリクエストからレスポンスまでの時間だと思う。米yahooの強力なサーバーなので正規表現でページ解析する処理時間はたぶん無視していい。
今回上の方で4つに分けているように、早めにsplitモジュールで分けてそれぞれでHTTPリクエストを発行させてレスポンス待ちにすると速い。splitで分けられたそれぞれと、Loopモジュール内でFetch PageやFetch Feedモジュールは自動的にpipes内で非同期処理になってそうな気がする。
※この時どれかで取得した結果を他のsplit先に使おうとすると、結局縦に1本につなげているのと変わらなくなってしまうので注意しましょう
pipesから他のサイトへのアクセスについては、twitterとyahooが地理的に遠くないからなのか制限が緩くなっているのかよくわからないが、3秒で100アクセスぐらいしてるのにエラーはでない。

