GISTで画像認識
せっかくなのでGISTで画像認識をやってみます。
GISTは画像全体のシーン認識を目的に設計されており、このタスクに特に適しているとされています。
まず、GISTの開発者が提供している、8クラスのシーン画像データセットで試してみます。
以下のページからImages.zipを落としてきて展開します。
http://people.csail.mit.edu/torralba/code/spatialenvelope/
一つのディレクトリの中に全ての画像が置かれていますが、扱いやすいようにクラスごとに分けることにします。
$ cd spatial_envelope_256x256_static_8outdoorcategories $ find . -name "*jpg" | xargs -l -i basename {} | cut -d'_' -f1 | sort | uniq | xargs mkdir $ mv mountain*jpg mountain $ mv opencountry*jpg opencountry $ mv forest*jpg forest $ mv coast*jpg coast $ mv tallbuilding*jpg tallbuilding $ mv street*jpg street $ mv insidecity*jpg insidecity $ mv highway*jpg highway
例のコードはppm/pgmしか読めないので、ImageMagickで変換します。
ヘッダにコメント領域が残ってるとおかしくなることがあるので-stripをつけます。このデータセットの画像は256x256のサイズにもともと揃えてあるので、サイズに関してはこのままで大丈夫です*1。
$ find . -name "*jpg" | sed 's/\.jpg//g' | xargs -l -i convert -strip "{}.jpg" "{}.ppm" $ find . -name "*jpg" | xargs rm
準備ができたので、データセットの特徴を抽出します。perlで適当にスクリプトを書いてみました。
#!/usr/local/perl use strict; use List::Util; my $Ntrain=100; my $imgdir="/home/XXX/spatial_envelope_256x256_static_8outdoorcategories"; my $cmd="/home/XXX/lear_gist-1.1/compute_gist"; opendir(IMGDIR, $imgdir) or die($imgdir); my @classes = readdir(IMGDIR); close(IMGDIR); open(TRAIN, ">train.txt"); open(TEST, ">test.txt"); foreach my $class(@classes){ next if ($class =~ /^\./ || -f $class); opendir(CLASS, "$imgdir/$class"); my @files = readdir(CLASS); closedir(CLASS); @files = List::Util::shuffle @files; my $count = 0; foreach my $file(@files){ next if( -d "$imgdir/$class/$file"); print "$imgdir/$class/$file\n"; open(GIST, "$cmd $imgdir/$class/$file |") or die("$cmd"); my $gist = <GIST>; close(GIST); next if(!$gist); if($count<$Ntrain){ print TRAIN "$file $class $gist"; }else{ print TEST "$file $class $gist"; } $count++; } } close(TRAIN); close(TEST);
$imgdir以下におかれたディレクトリから、各カテゴリの学習サンプルを一定数サンプリングし、残りをテストサンプルとしています。
なんかオーバーヘッドで時間とられているような気もしますがとりあえず無視。
無事特徴もとれたので、RのSVMで識別をやってみます。
kernlabパッケージが必要なのでインストールします。
> install.packages('kernlab')
作った特徴ファイルを読み込んでSVMを実行します。
> library('kernlab') > train <- read.table('train.txt') > test <- read.table('test.txt') > ot8.svm <- ksvm(train[,2]~.,data=train[,3:962],cross=5) > ot8.pre <- predict(ot8.svm,test[3:962]) > (ot8.tab <- table(test[,2],ot8.pre)) > mean(diag(ot8.tab)/rowSums(ot8.tab)) [1] 0.8074061
という感じで、識別率は約80%となりました。本当はサンプルを入れ替えながら何回か試行するべきですが。
ついでにRandomForestも試してみました。パラメータはデフォルトで。
> install.packages('randomForest') > library('randomForest') > set.seed(20) > ot8.rf <- randomForest(train[,2]~.,data=train[,3:962]) > ot8.pre <- predict(ot8.rf,test[3:962]) > (ot8.tab <- table(test[,2],ot8.pre)) > mean(diag(ot8.tab)/rowSums(ot8.tab)) [1] 0.7760976
ちなみに、前回やった判別分析だと
library('MASS') set.seed(20) ot8.lda <- lda(train[,2]~.,data=train[,3:962]) ot8.pre <- predict(ot8.lda,test[3:962]) (ot8.tab <- table(test[,2],ot8.pre$class)) mean(diag(ot8.tab)/rowSums(ot8.tab)) [1] 0.4530859
けっこう次元が大きいので、正則化項を入れてやらないと無理っぽいです。