超ひっさびさ!!!

javascript画像処理するブログの14回目?
数ヶ月放置でしたが何もしていなかったわけではなく、ちびちびとOpenCVjsの機能を追加しようとしては挫折をするという迷走をしていました。

ここ数ヶ月は顔認識やらに使われる機械学習の学習をしておりその機能がようやく実装されたので調子のってブログ更新です。

しばらく僕が歩んだ苦難の道を書いてるだけなので、さっさとコードが見たい人は読み飛ばして下さい。

とりあえず機械学習を学ぶにあたって参考にした本やリンクはこんな感じ。
はじめてのパターン認識/森北出版
¥3,150
Amazon.co.jp

機械学習の「」の字も知らん僕にはここからすでに読むの苦労しましたが雰囲気掴むには良かった気もします。
しかしこれを読むだけでは数年前から流行のサポートベクターマシン(SVM)とかいうのが全く実装できず、やむなく翻訳がダメだダメだと噂の次の赤本も読んでみました。

サポートベクターマシン入門/共立出版
¥4,200
Amazon.co.jp
うん、まあ、噂通り読みづらいわこれw
単純に前から読んでいったらあっという間に挫折するとこだったのをこちらのブログに救われました。

SVMの定番入門書「サポートベクターマシン入門(赤本)」の読み方

上の赤本の挫折しない読み方を記載してくれています。
リンク先の通りに読めばまぁ1~2日長く粘って挫折できます。
んで、まあ赤本の付録のコードとか読んでもいまいちすっきりせず、結局さらに次のブログを読んでようやくJSに移植できたような気になれてSVMが分かった気になりました

SVMコード

いやもうホント色んな人に感謝です。

ほんじゃようやく機械学習の説明!!今回学習させるデータはこれ!

学習データ


なんのこっちゃよく分からないでしょうけど、画像内の各点が学習データ座標を特徴量としてクラス分けされるようにします。

分かりづらいかもしれませんが左上にが集中してます。

ようやくサポートベクターマシンjavascriptコード!!



function SVM(imgId, iplImage){

var trainss = new Array(); //学習データの2次元配列
var answers = new Array(); //クラスデータ
//--------------------------(1)学習データの読み込み----------------------------
for(var i = 0 ; i < iplImage.height ; i++){
for(var j = 0 ; j < iplImage.width ; j++){
//各座標の画素を取得し赤クラスと青クラスに分類
var ji = (j + i * iplImage.width) * CHANNELS;
var r = iplImage.RGBA[ji];
var g = iplImage.RGBA[1 + ji];
var b = iplImage.RGBA[2 + ji];
var answer = 0;
if(r > 200 && g < 50 && b < 50) answer = 1;
else if(r < 50 && g < 50 && b > 200)answer = -1;
if(answer != 0){
//座標を学習データに代入
var trains = new Array(j/iplImage.width, i/iplImage.height);
trainss.push(trains);
answers.push(answer);
}
}
}
        //------------------------(1)ここまで------------------------------

        //-------------------(2)SVMの各種パラメータ設定----------------------------
//SVMクラスに読み込ませる終了条件クラスのインスタンスを生成
var termcriteria = new CvTermCriteria();
termcriteria.max_iter = 100000; //最大繰り返し回数
termcriteria.epsilon = 0.1; //C-SVMのイプシロン
//SVMクラスに読み込ませるパラメータクラスのインスタンスを生成
var cvSVMP = new CvSVMParams();
cvSVMP.kernel_type = CV_SVM_KERNEL_TYPE.POLY; //SVMのカーネルの種類
cvSVMP.degree = 4.0; //カーネルで使われるチューニングパラメータ1
cvSVMP.gamma = 1.0; //カーネルで使われるチューニングパラメータ2
cvSVMP.coef0 = 1.0; //カーネルで使われるチューニングパラメータ3
cvSVMP.C = 5; //C-SVMで使われるC
cvSVMP.term_crit = termcriteria; //終了条件クラスを代入
cvSVMP.tolerance = termcriteria.epsilon; //許容する計算誤差
cvSVMP.minLearnData = 10; //学習データの最低数
        //------------------------------(2)ここまで------------------------

         //---------------------------------(3)SVMで学習--------------------------
//SVMクラスのインスタンスを生成
var cvSVM = new CvSVM();
//学習
cvSVM.train(trainss, answers, null, null, cvSVMP);
//---------------------------------(3)ここまで---------------------------------

         //------------------------------(4)SVMで予測-----------------------------
//学習データを用いて画像の座標の色を予測する
for(var i = 0 ; i < iplImage.height ; i++){
for(var j = 0 ; j < iplImage.width ; j++){
//予測対象の座標を特徴量として配列に代入
var inputs = new Array(j/iplImage.width, i/ iplImage.height);
//予測
var predict = cvSVM.predict(inputs, trainss);
var r = 255; var g = 255; var b = 255;
//予測結果が1なら赤、0なら青とする
if(predict >0){
r = 255; g = 0; b = 0;
}
else{
r = 0; g = 0; b = 255;
}
//特徴量となった座標の色に代入
var ji = (j + i * iplImage.width) * CHANNELS;
iplImage.RGBA[ji] = r;
iplImage.RGBA[1 + ji] = g;
iplImage.RGBA[2 + ji] = b;
}
}
//------------------------------(4)ここまで-----------------------------

        //画像を出力
cvShowImage(imgId, iplImage);
}




例によってOpenCVjsを使ってますよ。

まあ書いてる通り、全体の概要は
(1)学習データの読み込み
(2)SVMの各種パラメータ設定
(3)SVMで学習
(4)SVMで予測
です。

実際に画像から学習データを作成し、その他の座標の色を予測したらこんな感じになりました。

推測結果


まぁなんとなくちゃんと非線形に学習できたかなぁって!
ただ学習処理の内部で乱数を使っており、毎回学習と予測結果が変わります
酷い時は全然ちゃんと学習してなかったり...
これについてはたぶん(2)SVMの各種パラメータ設定を何も考えずに調整しちゃったせいかなあと思ってますがどうなんでしょう?
パラメータの設定も交差検定?とかちゃんとした手法があるらしいですよ、まだ調べてないけど!

とりあえず見た目はつまらないですが画像認識の第一歩となりました!
あとは
HOGとか実装すればjavascript顔認識とかの画像認が出来るようになる…といいなあ…