jQueryのアニメーションを使ってカジュアルゲームを作ってみる

JavaScriptは以前に少しだけいじったことがありましたが,なんとなく書き方がややこしくて頓挫してしまった覚えがあります.
ただ,jQueryを使ってみたら,今までのイメージを覆すくらいJavaScriptをラクチンに感じました.
以下はjQueryを使って簡単なクリック型のカジュアルゲームを作ってみた例です.

jsdo.itに置いていたのですが,ちゃんと自分の作ったものを一元管理して置けるようにサーバをレンタルして,そこに移しました.

ゲームソース - Find Me -

http://glass5er.sitemix.jp/apps/findme

JavaScript, CSS, htmlあわせて250行ほどで書けました.
もっと書き方がわかってくれば,半分くらいのコード量でできるのかもしれません.

ゲーム内容

並べられた画像のなかから1つだけ違うものを探してクリックするゲームです.
正解をクリックすると点数が入り,制限時間内に何pt取れるかチャレンジする形式です.
詳細は少し遊んでもらえればわかると思います.
制限時間は,自分が調子のいいときに200pt取れるくらいを目安に設定しました.

jQueryの使用

やっていることは,htmlファイル内でタグ付けした要素に対して
CSSの属性をいじったり,アニメーションさせたりといった無いようです.
jQueryだと要素へのアクセスが下記のような記述で簡単に行えるのがいい感じです.

$("ELEMENT").process

ELEMENTのところは,#idやclassなどを設定します.
processのところは,cssやslideDownなど,jQueryAPIに実装された関数を呼び出します.
詳しくは下記で調べて使いました.
http://semooh.jp/jquery/
slideDownやhideは非常に便利ですね.巷では常識なのかもしれませんが...

JavaScriptソース

JavaScriptのソースを以下に載せます.
ほかにも色々つくってみたいです.

var total_point = 0;
var timer_limit = 600;
var timer_enable = false;
var timer_current = 0;
var timer_initialized = false;
var title_text = " - Find Me - ";
var prev_ans = 0;

$(function() {
  //  @FIRST : show title  //
  title();
});

function title()
{
  timer_initialized = false;
  timer_enable = false;
  total_point = 0;
  //  init point  //
  $("#point").text(total_point+" pt");
  //  set title  //
  $("#title").text(title_text).show();
  //  set start button  //
  $("#button").text("start")
    .unbind().click(function(){
      $("#title").hide();
      //  start game  //
      if(!timer_initialized) {
        mainframe();
        //  set timer veriables  //
        timer_initialized = true;
        timer_enable = true;
        timer_current = timer_limit;
        //  set timer image  //
        $("#bar_f").css("height", "100%").css("background","#a0e0a0");
        $("#button").css("-webkit-opacity","0");
        timerCountDown();
      }
    });
}

function timerCountDown()
{
  var timer_id = 0;
  timer_current -= 1;
  //  time over -> disable timer  //
  if(timer_current <= 0) {
    timer_enable = false;
    window.clearTimeout(timer_id);
  }
  //  enable timer  //
  if(!timer_enable) {
    //  init  //
    $("#bar_f").css("width", "100%").css("height", "auto").css("background","none");
    $("#button").css("-webkit-opacity","1.0");
    result();
  }else{
    //  count down  //
    $("#bar_f").css('width', parseInt(100*timer_current/timer_limit,10)+"%");
    timer_id = window.setTimeout(timerCountDown,50);
  }
}

function mainframe(){
  //  show point  //
  $("#point").text(total_point+" pt");
  //  reset stage propaties  //
  $("#stage *").hide();
  $("#stage td").removeClass("raw")
    .removeClass("sel")
    .addClass("uns");
  //  calc answer index (random)  //
  var elem_num = $("#stage td").size();
  var ofst = parseInt(Math.random()*(elem_num-1),10) + 1;
  var ans_idx = (prev_ans + ofst);
  if(ans_idx >= elem_num) ans_idx -= elem_num;
  //  set answer symbol (image)  //
  $("#stage td:eq("+ans_idx+")").removeClass("uns")
    .addClass("sel");
  //  animation  //
  $("#stage *").slideDown("slow");
  prev_ans = ans_idx;
  //  add action lister  //
  listener();
}

function listener(){
  //  when clicked  //
  $("#stage td").click(function(){
    var $this = $(this);
    //  if this==answer -> get point  //
    if($this.hasClass("sel")) {
      total_point += 10;
      mainframe();
    }
  });
}

function result() {
  //  hide stage  //
  $("#stage *").hide();
  $("#title").text("your score : "+ total_point + " pt")
    .show();
  //  reset button  //
  $("#button").text("reset")
    .unbind().click(title);
}

OpenCVでSVMを使った機械学習&推定

OpenCVは本当に便利ですね.
さまざまなツールが内包されていますが,その中でも機械学習に関する部分をいじってみようと思い,SVMに手をつけました.
やっていることはサンプルコードの域を脱していませんが,自分で書いてみることで使い方がわかってきました.
テーマは,「ぼけた画像(ピントが合っていない)とそうでない画像を自動的に区別する」です.
ためしに作ってみたものは性能的にイマイチでしたが,一応使い方をまとめておきます.

作ったもの

以下の機能をもつプログラムです.
・データセット(特徴量,ぼけているかどうかの判定)を読み込んで,ぼけているかどうかの判定基準を学習する
・学習した判定基準にしたがい,新たに読み込んだ特長量から画像のぼけ判定をする
ここにコミットしました.
https://github.com/glass5er/opencv-ml-svm
画像も載せたいですが,著作権的なことに触れるとまずいので,データセットだけコミットしました.

プログラムの流れ

学習フェイズ

まず,データセットを読み込みます.
最終的にsvmに渡せれば何でも良いですので,CSVファイルから読み出す形にしました.
必要なのは以下です.

  • 判定フラグ(整数値)
  • 特徴量データ(実数)

データの個数に決まりはありませんが,学習シナリオごとに個数が異なるのはNGです.

  //  training data buffer  //
  std::vector< int > flagset;
  std::vector< std::vector<double> > dataset;
  read_csv(fname_train.c_str(), flagset, dataset);
  const int lines_tr = dataset.size();
  const int segments = dataset[0].size();
<||
読み出したデータを行列に格納し,学習します.
train一発で学習できるのは便利です.
>|cpp|
  //  dataset -> matrix  //
  cv::Mat flagmat(lines_tr, 1, CV_32SC1);
  cv::Mat datamat(lines_tr, segments, CV_32FC1);
  for(int i=0; i<lines_tr; i++) {
    flagmat.at<int>(i,0) = flagset[i];
    for(int j=0; j<segments; j++) {
      datamat.at<float>(i,j) = dataset[i][j];
    }
  }
  //  training (SVM)  //
  CvSVM svm;
  svm.train(datamat, flagmat);
推定フェイズ

学習のときと同じように,CSVからデータを読み込んでいます.
当然ながら,特徴量の個数は学習時と同じでないといけません.

  //  prediction data buffer  //
  std::vector< int > flag_tmp;
  std::vector< std::vector<double> > data_tmp;
  read_csv(fname_pred.c_str(), flag_tmp, data_tmp);
  const int samples = data_tmp.size();

  //  each sample -> predict //
  cv::Mat sample(1, segments, CV_32FC1);
  for(int i=0; i<samples; i++) {
    for(int j=0; j<segments; j++) {
      sample.at<float>(0,j) = data_tmp[i][j];
    }
    //  prediction (SVM)  //
    float res = svm.predict(sample);
    cout << "pred result[" << i << "] = " << res << endl;
  }

準備した特徴量

以下のように,画像の書く画素について周辺1画素,あるいは2画素内での
差分絶対値の最小値をとり,そのヒストグラムを特徴量としました.
(こんなので判別できるなら機械学習いらないでしょうけど,お試しということで.)

推定結果

学習シナリオ192個(ぼけているx96個,ぼけていないx96個)を使い,学習した基準に対し,
自身が正しく判定されるかを確かめました.
つまり,学習と同じデータを推定にも使ってみました.
その結果,

  • ぼけている画像 : 正解80/96, 不正解16/96
  • ぼけていない画像 : 正解76/96, 不正解20/96

という,あまりよろしくない識別率が出ました.
特徴量しだいでどうにでもなる部分だと思うので,再考してみようと思います.

最後に

SVMは,ツールとしては非常に簡単に扱えました.
ただ,どう使えば性能を最大限に引き出せるかは,まだまだ修行が必要みたいです.
最初は別の機械学習アルゴリズムであるBoostingで同じことをやろうとしていましたが,
今回の特徴量とはあまりにも相性が悪かったのでやめました.
次はBoostingで何かやってみたいと思います.

CentOS5にRedmine1.3をインストール

仕事で使えるとよいなと思い,プロジェクト管理ツールのRedmine (http://redmine.jp/)をインストールしました.
Ubuntu10.11とCentOS5にインストールしましたが,特にハマるポイントの多かったCentOS5について記しておきます.
インストールしたバージョンは1.3です.

必要なツール,バージョン(重要!)

Redmineはインストールするためのバージョン指定があります.
公式ページ(http://redmine.jp/guide/RedmineInstall/)に各バージョンの対応が記されています.
とりあえず,Redmine1.3の場合には,以下の順にインストールしたらうまくいきました.
ruby 1.8.7 (ソースコードから)
rubygems 1.3.7 (ソースコードから)
mysql (yumから)
・rack 1.1.1 (rubygemsから)
・rake 0.8.7 (rubygemsから)
rails 2.3.14 (rubygemsから)

ruby 1.8.7のインストール

CentOSのパッケージツールで手に入るrubyは,デフォルトとRPMForgeをあわせても最新版が1.8.5です.
後述するrubygemsのバージョンあわせのあたりで必要になる場合があるので,ここで1.8.7をインストールしておくのが無難です.
ただし,Redmineruby 1.9以上だと動かないので,新しすぎてもダメです.

ソースは公式からもらってきます.今回は1.8.7-p358(ftp://ftp.ruby-lang.org/pub/ruby/1.8/ruby-1.8.7-p358.tar.gz)を使いました.
解凍して,以下のとおりインストールします.

tar zxf ruby-1.8.7-p358.tar.gz
cd ruby-1.8.7-p358
./configure --prefix=/usr
make
sudo make install

sudo 設定されていない場合は,su で管理者権限になってから make installでも良いです.

rubygems 1.3.7のインストール

公式の案内には,1.3.7以上が必要であることと,Railsとのバージョンの相性がある旨について書かれています.
今回は無難に1.3.7をインストールしました.
CentOSのデフォルトリポジトリ+RPMForgeでのrubygemsの最新バージョンは1.3.2なので,こちらもソースからのビルドです.
入手先はこちら(http://rubyforge.org/frs/download.php/70696/rubygems-1.3.7.tgz)

tar zxf rubygems-1.3.7.tgz
cd rubygems-1.3.7
sudo ruby setup.rb
rubygemsのバージョンを戻す場合

普段もっと新しいrubygemsを使っている場合,インストールの最後のあたりにエラーがでてしまうことがあります.
その場合,rubygemsが新しすぎて起こる問題なので,以下のようにバージョンを戻します.

sudo gem install rubygems-update -v 1.3.7
sudo update_rubygems

MySQL をyumからインストール

MySQLは,yumから手に入るもので特に問題はありません.

sudo yum install mysql mysql-devel mysql-server

rake, rack, rails, (mysql) をgemからインストール

Redmineに必要なもののなかで,rubygemsで手に入るものはまとめてインストールします.

sudo gem install rack -v 1.1.1
sudo gem install rake -v 0.8.7
sudo gem install rails -v 2.3.14

さらに,後々のrailsでの作業のために,MySQLをgemと関連付けておきます.

sudo gem install mysql

あとは公式の指示通りに行う

ここまでで,公式ページ(http://redmine.jp/guide/RedmineInstall/)の「インストール」の流れに乗る準備ができました.
あとは書かれている指示のとおりに進めていけば,Redmineが実際に動作するところまでいけます.

一応,コマンドだけ羅列しておきます.

tar zxf redmine-1.3.1.tar.gz
cd redmine-1.3.1
sudo /sbin/service/mysqld start
mysql -u root -p
(mysqlコマンド)
create database redmine character set utf8;
create user 'redmine'@'localhost' identified by 'my_password';
grant all privileges on redmine.* to 'redmine'@'localhost';
(mysqlおわり)
vim config/database.yml
(yml編集)
sudo rake config/initializers/session_store.rb
sudo RAILS_ENV=production rake db:migrate
sudo RAILS_ENV=production rake redmine:load_default_data
mkdir tmp public/plugin_assets 
sudo chown -R redmine:redmine files log tmp public/plugin_assets
sudo chmod -R 755 files log tmp public/plugin_assets
sudo ruby script/server webrick -e production -p 3000

これで個人でもプロジェクト管理ができるようになって,開発や実装の効率があがるはず.

OpenCVのコマンドラインパーサ

OpenCVのコマンドラインパーサを使って,オプションを設定します.
基本的な表記ルールに従う必要がありますが,慣れればベタに書くよりもずっと楽です.

#include "opencv2/opencv.hpp"

#include <iostream>
#include <string>

using std::string;
using std::cout;
using std::endl;

void help()
{
  cout << endl
       << "OpenCV command-line parser test." << endl
       << "Call:" << endl
       << "./parser -b -i=2 -f=5.3 str1str in3val" << endl
       << endl;
}

const char* keys = 
{
  //  keys  //
  //  "{short tag|long tag|default value|notification}"  //
  "{1|||sample string without option tag}"
  "{2||-100|sample int without option tag}"
  "{b|bool|false|sample boolean}"
  "{i|int|0|sample int}"
  "{f|float|0.0|sample float}"
};

int main( int argc, const char** argv )
{
  //help();

  //  parser  //
  cv::CommandLineParser parser(argc, argv, keys);

  //  without 
  string str_1 = parser.get<string>("1");
  int   in_2 = parser.get<int>("2");
  //  value as corresponding type  //
  bool  bo_b = parser.get<bool>("b");
  int   in_i = parser.get<int>("i");
  float fl_f = parser.get<float>("f");
  //  value as string  //
  string str_b = parser.get<string>("b");
  //string str_i = parser.get<string>("i");
  //string str_f = parser.get<string>("f");

  cout << "str_1 = " << str_1 << endl;
  cout << "in_2 = " << in_2 << endl;

  cout << "bo_b = " << bo_b << endl;
  cout << "in_i = " << in_i << endl;
  cout << "fl_f = " << fl_f << endl;

  cout << "str_b = " << str_b << endl;
  //cout << "str_i = " << str_i << endl;
  //cout << "str_f = " << str_f << endl;

  return 0;
}

オプションなし実行結果(デフォルト値が使われます)

./parser

str_1 = 
in_2 = -100
bo_b = 0
in_i = 0
fl_f = 0
str_b = false

オプションあり実行結果

  • 値が必要なタグについては,=で結んでも良いし,スペースで区切っても良いようです.
./parser string_001 333 -b=true -i=28 -f=7.5

str_1 = string_001
in_2 = 333
bo_b = 1
in_i = 28
fl_f = 7.5
str_b = true

./parser string_001 333 -b true -i 28 -f 7.5

str_1 = string_001
in_2 = 333
bo_b = 1
in_i = 28
fl_f = 7.5
str_b = true

無効な書き方

  • タグなし引数がintでパースできないときは,0が返ってきました.
  • 値が必要なタグに値をつけないと,次のタグを誤って値としてパースしまいました.
  • boolでのパースについては,値をつけなくとも正しく動作しました.
./parser string_001 not_int -b -i -f

str_1 = string_001
in_2 = 0
bo_b = 1
in_i = 149075172
fl_f = 0
str_b = true
<追加dump出力>
str_i = -f
str_f = 0.0

このパーサだけでも,かなり便利です.

はてな記事のなかで数式を使う

 f(x) = \frac1{\sigma\sqrt{2\pi}} \int_{-\infty}^x e^{-\frac{(t-\mu)^2}{2\sigma^2}}dt
[ tex: f(x) = \frac1{\sigma\sqrt{2\pi}} \int_{-\infty}^x e^{-\frac{(t-\mu)^2}{2\sigma^2}}dt ]

[ tex: なんとかかんとか ] と書くと良いようですね.
行頭と[]周辺のホワイトスペースをけずると数式表示できます.
逆に,スペースを入れておけば数式には変換されません.

上記は,mimeTex ( http://www.forkosh.com/mimetex.html )のページのイントロダクションにあった数式を貼り付けました.
Texですね.良いです.

OpenCVをインストールしたあとにサンプルを動かすまで(手戻りあり)

先日インストールしたOpenCVですが,実際にサンプルを動かすところにまではいたらなかったので,その続きをやりました.
C++サンプルフォルダの中にあった,エッジ検出プログラム(edge.cpp)をコンパイルして動かすところまでできました.
ここまでできれば,あとはAPIなど見ながら学習に専念できるはず.

一口にやりましたとは言ったものの,いくつかつまづきポイントがありました.

Makefile

コンパイルをする際にヘッダの読み込みやライブラリのリンクが必要になるので,Makefileに記述を追加しました.
(もととなるMakefileは,"Makefile 書き方"で検索しててきとうにつくりました.)

CXXFLAGS = `pkg-config opencv --cflags`
LDFLAGS = `pkg-config opencv --libs`

上の行が,OpenCVのインクルードヘッダなどの情報をまとめて設定してくれるコマンドで,
下の行はライブラリの設定をしてくれるコマンドです.
ちなみに ` はバッククオート(Shift+@)で,``で囲まれた部分をコマンドとして実行するものです.
実際に``の中身をシェルで実行すると,フォルダのパス情報などが出力されます.

これはもちろん, pkg-config がインストールされていないと使えないコマンドなので,ない場合はインストールします.

sudo apt-get install pkg-config

そして$(CXXFLAGS),$(LDFLAGS)あたりをコンパイル設定に加えて,コンパイル.
とりあえず通りました.

PATHを通す

さっそく実行してみると,こんなエラーがでました.

  • ./sample: error while loading shared libraries: libopencv_core.so.2.3: cannot open shared object file: No such file or directory

引数なしで実行したので,使い方が表示されるのを期待しましたが,それ以前の問題のようです.

soファイルが見つからないといわれました.
インストール先にないのかと思ったら,そこにはちゃんとファイルがあって,インストール先にパスを通し忘れていることが原因でした.

というわけで,次の記述.

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib

(毎回やるのは面倒なので,${HOME}/.bashrc に直接書いてしまいました.)

これで,/usr/local/lib にインストールされたOpenCVを参照しにいけるようになりました.

GTK2.0-devのインストール

さて,もう動くだろうと思って,サンプルフォルダからコピーしてきた画像とともに実行しました.

エラーがでました.

どうやら実行結果をウインドウ表示しようとしてエラー終了しているようです.
詳しい内容はログをとっていなかったので忘れてしまいましたが,"libgtk2.0-dev is not implemented. install it."的なことが書いてあった気がします.

sudo apt-get install libgtk2.0-dev

気づくのが非常におそかったのですが,GTKが入っている状態でOpenCVを make install しないといけないですね.
以下,前回のおさらいなので早送り.

cd lib-opencv-latest
cmake -DCMAKE_BUILD_TYPE=RELEASE -DWITH_CUDA=OFF .
make
sudo make install

今後こそ,ということで実行しました.

OKです.

サンプル left01.jpg に対して,サンプル edge.cpp のコンパイル結果を適用した図.

OpenCVの最新版ソースコードをSubVersionから入手してLinux(Ubuntu)にインストール

たった今,OpenCVの最新版2.3をインストールしました.
特にはまるところが無かったので,15分くらいで終わりました.

準備したもの

ここからSVNを使ってチェックアウトしました.
https://code.ros.org/svn/opencv/trunk

SVN

家のUbuntuには,svnが入ってなかったので,ますはそれからインストール.

sudo apt-get install subversion

sudoで管理者権限にしないとインストールできないと思います.
パスワードを入力して,待つ.

次に,最新版のOpenCVのソースコードを入手.

svn co https://code.ros.org/svn/opencv/trunk ./lib-opencv-latest

"co" は "チェックアウト".データをもらってくるコマンド.
最後の./lib-opencv-latest はダウンロード先のフォルダ名.名前はなんでもよいです.

そこそこ(数分くらい)待って,"チェックアウトしました" とのことなのでフォルダの中を見て,
Makefileもconfigure(マシン設定からMakefileを自動生成してくれるやつ)も無くて少し困りました.

CMAKE

"cmake" と名前がついているものが大量にあったので,これがconfigureの代わりということなのでしょうか.
というわけで,cmakeもインストールしました.

sudo apt-get install cmake

前述したsvnとほぼ同じです.
待っているだけでインストールできました.

OpenCV

ツールがそろったので,いよいよOpenCVの「インストール作業に入ります.

まずはcmakeにMakefileを作ってもらいます.

cmake -DCMAKE_BUILD_TYPE=RELEASE -DWITH_CUDA=OFF .

下記サイトを参考に,自分に関係ありそうなオプションだけつけて実行しました.
http://www.kkaneko.com/rinkou/opencv/opencvinstalllinux.html
ちなみに,コマンドの最後の"."は,"カレントフォルダにMakefileを作ってください"という意味.

Makefileができたので,もうあとはいつもどおりに,

make

(コンパイルが終わるまで待つ.パーセンテージが出るのでだいたい時間が読める)

sudo make install

(管理者権限でOpenCVをインストール)
で終了でした.

らくちん.

次は使ってみます.