超ひっさびさ!!!
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で顔認識とかの画像認識が出来るようになる…といいなあ…