3Dの素人がBundlerでVLFeatを使ってみた

この記事について

この記事はComputer Vision Advent Calendar 2012の12月10日分の記事として書いています.(一部はスタバにて,Mac Book Proで書いています.内容のBundler利用はLinux (Fedora 17) で行なっています.)
さて,今回の内容は,Bundlerを動かすこと,そしてその中で出てくるプログラムの一部にVLFeatというプログラムを利用する,という内容です.

Bundlerとは

Bundlerとは,Rubyのパッケージを管理するgemのラッパーで…ではなく,画像から3次元モデルを作るStructure from Motion (SfM)をするためのツールです.
オフィシャルのサイトにも

Bundler is a structure-from-motion (SfM) system for unordered image collections (for instance, images from the Internet) written in C and C++. An earlier version of this SfM system was used in the Photo Tourism project.

とあり,C/C++で実装されており,Photo Tourismというプロジェクトでも利用されていました.
で,なぜBundlerという名前かというと,このStructure from Motionの一連の処理の中でBundle Adjustmentという3次元と2次元の対応関係を計算する処理(ここがメイン?)があり,そこから名付けられているのだと思います.
基本的な処理の流れは,

  1. 特徴点を抽出する
  2. 複数の画像間で対応する特徴点を探索する
  3. 対応づいた特徴点をもとにBundle Adjustmentを実行する

となっています.
Bundlerのページからダウンロードできるコード(ver. 0.4)では,RunBundler.shというスクリプトにより,この一連の処理を行なっており,特にステップ1,2に対してはSIFT特徴を使って検出と対応関係の獲得を行なっています.
Bundlerの使い方自体は,満上師匠先生による解説が参考になります(2017.06.01リンク張り替え).
また,Bundle Adjustment自体の計算等については,岡谷先生のチュートリアル(もしくはコンピュータビジョン最先端ガイド)が参考になります.
オリジナルのBundlerの1,2のSIFTは,SIFT特徴を発明したD.Lowe先生の実装が利用されていますが,満上さんの話によるとR.Hess氏の実装(OpenSIFT)も簡単に利用できるとのことです.
今回,さらに別のSIFT実装であるVLFeatを利用してみようという話です.
ちなみに,Bundlerをコンパイルする時,新しいコンパイラでは基底クラスのコンストラクタ呼び出しの仕方でエラーが出ます.
src/BundlerApp.h の 620行目を消し,619行目に基底クラスのコンストラクタ呼び出しを追加してください.

VLFeatとは

VLFeatは様々な特徴量やアルゴリズムが実装されたライブラリです.

The VLFeat open source library implements popular computer vision algorithms including HOG,SIFTMSERk-meanshierarchical k-meansagglomerative information bottleneckSLIC superpixels, and quick shift. It is written in C for efficiency and compatibility, with interfaces in MATLAB for ease of use, and detailed documentation throughout. It supports WindowsMac OS X, and Linux. The latest version of VLFeat is 0.9.16.

HOGとかSIFTとかMSERとか色んな特徴量が実装されています.
(あれ…「OpenCVでええやん」という声が 聞こえてきた…)
まぁこれをダウンロードしてきてコンパイルすると,D.Lowe先生のSIFT実装の実行プログラムとほぼ同じ入出力のsiftという実行プログラムができます.
今回,このVLFeatで実装されているsiftプログラムをBundlerで使ってみます.

BundlerでVLFeatのsiftを使う

bundlerを展開したディレクトリ内のbinディレクトリにD.Lowe’s siftの代わりにVLFeatのsiftを置くだけで済む…かと思いきや….
D.Lowe’s siftとVLFeatのsiftの微妙な出力の違いでど嵌りしました.
だいぶ戦った結果,ステップ1で一旦出力するsift特徴量を記述したファイル(hoge.jpgが入力ならばhoge.key.gz)の形式が微妙に違うことがわかりました.
hoge.sift.gzをgunzipしてできたhoge.siftを見てみると,VLFeatに入っているsiftプログラムの出力は各行に

x y scale orientation 128次元がスペース区切り

が出力されていますが,D.Lowe’s siftの出力には,先頭行に

特徴点数 次元数

の行があります.さらに,残りの各行は

20次元がスペース区切り
20次元がスペース区切り
20次元がスペース区切り
20次元がスペース区切り
20次元がスペース区切り
20次元がスペース区切り
8次元がスペース区切り

の形になっています.
Bundlerではこのファイルを開き,まず特徴点数を読み込んでから特徴点の配列を確保する仕様になっているので, VLFeatの入出力を少しいじる必要があります.
(Bundler側の読み込みコードを修正するという手もあるが,コードがあまりにもクソアレだったのでおすすめできない.)

sift入力の修正

D.Lowe’s siftの場合,入力としては標準入力からpgmフォーマットの画像を読み込み,特徴量を標準出力へ出力する仕様になっているのに対し,VLFeatのsiftは,第1引数で与えられたhige/hoge.pgmを読み込み,hoge.keyをカレントディレクトリに出力する仕様になっている.
そのため,Bundlerのbin/ToSift.shファイルの36行目を次のように修正する.
修正前

echo "mogrify -format pgm $IMAGE_DIR/$d; $SIFT < $pgm_file > $key_file; rm $pgm_file; gzip -f $key_file"

修正後

echo sift_file=`echo $d | sed 's/jpg$/sift/'`
echo "mogrify -format pgm $IMAGE_DIR/$d; $SIFT $pgm_file; mv $sift_file $key_file; rm $pgm_file; gzip -f $key_file"

sift出力結果の修正

変換の修正ですが,特徴量ファイルはgz形式で圧縮されているので(ホントは上のToSift.shでgzipしなければ良いだけだけれど),

  1. gunzipで解凍
  2. 特徴量ファイルを修正(先頭行に特徴点数と次元数を追記)
  3. 各行を20次元ずつに分割
  4. gzipで圧縮

という手順になります.
まず,ステップ2,3の部分をpythonで書いてみると

#!/usr/bin/env python
import sys
i=0
buf=[]
for line in sys.stdin:
    i+=1
    l=line.split(" ")
    buf.append(" ".join(l[0:4]))
    l=l[4:]
    for j in range(6):
        buf.append(" ".join(l[j*20:(j+1)*20]))
    buf.append(" ".join(l[120:128]))
print i,"128"
print "\n".join(buf)

となります.
これをbundlerのbinディレクトリに,vlfeat2lowe.py として保存し,実行権限を与えます.
あとは,これに解凍/圧縮と,各ファイルに対して処理できるように ループを回して,

for img in `ls -1 $IMAGE_DIR | egrep ".jpg"`
do
        echo $img
        name=`basename $img .jpg`
        gunzip $IMAGE_DIR/${name}.key.gz
        $BASE_PATH/bin/vlfeat2lowe.py < $IMAGE_DIR/${name}.key > tmp.key
        mv tmp.key $IMAGE_DIR/${name}.key
        gzip -f $IMAGE_DIR/${name}.key
done

このコードをRunBundler.shの66行目あたりに突っ込めば動くようになるはずです.
ホントはもうちょっとスマートに書きたいが…ひとまず突貫工事ということで.
スマートに書きなおすには大工事が必要そうだったので.

結果の比較

D.Lowe’s siftとVLFeatのsiftでBundlerを実行した結果の比較として,両手法で推定した3次元推定結果を以下に示す.
ただし,Bundlerの後処理として,密な3次元点群を得るために古川氏PMVS2を実行している.
可視化にはmeshlabを利用し,スクリーンショットを撮った.
まず入力画像はこちら

まずはD.Lowe’s siftを用いた結果.
こっちのほうが圧倒的に処理は速かった.
結果の画像はこちら.

次にVLFeatのsiftを用いた結果.
検出されるキーポイント数が全然違う…閾値が違うのかな?
結果の画像はこちら.

違いが分からん…pmvs2するべきではなかった.
pmvs2前の頂点数を比較すると,D.Lowe’s sift版では1459点に対してVLFeat版では1907点でした.
SIFTの検出キーポイント数が多い分,対応付けも多くとれたのかな.

終わりに

あーあ,完全にネタかぶっているブログを見つけてしまった….
zcatなんてコマンドがあったのか…
次やるときはもうちょっとスマートに書きたい.

コメントする