Pipe Renderでタグ「ARToolKitの解析」が付けられているもの

作成した処理の可視化プログラムを公開いたします。

ARToolKitAlgo1.0_080510.zip


今回のプロジェクトは"Microsoft Visual C++ 2008 Express Edition"で開発いたしました。
ライブラリに、いくつかコメントを追加しましたが、不完全で間違っている可能性があります。


--- <実行ファイルの使い方> ---


○変換行列の計算プログラム

・とりあえず実行
0.カメラにマーカーが映るようにします
1.画像が表示されているウィンドウを選択して、[C]キーを押し、3Dモデルのみの画面が大きく表示されるようにします。
2.画面内で、右クリックしながら3Dモデルをズームアウトして、仮想的なカメラが表示されるようにします。
3.[1]キーを押して、仮想的なディスプレイを表示します。
4.[2]キーを押して、ディスプレイに映ったマーカーを表示します。
5.[3]キーを押して、マーカーの辺1を伸ばした面を、1つ表示します。
6.[4]キーを押して、面1の法線を表示します。
7.[5]キーを押して、辺1の向かいの辺での面と法線を表示します。
8.[6]キーを押して、2つの法線の外積を求めます。
9.[7]キーを押して、残りの面と法線を表示します。
10.[8]キーを押して、その外積を表示します。
11.[9]キーを押して、2つの外積の外積を表示します。

・その他の操作
[0]キー:処理の表示を初期状態に戻す
[b]キー:立方体オブジェクトの表示/非表示
[m]キー:マーカーオブジェクトの表示/非表示


○マーカーの認識プログラム

[1]キー:2値化した画像を表示します。
[2]キー:ラベリングした画像を表示します。
[3]キー:頂点1を表示します。
[4]キー:輪郭を表示します。
[5]キー:頂点2を表示します。
[6]キー:頂点3,4を表示します。
[0]キー:表示を元に戻します。
[c]キー:画像の位置を変更
[o]キー:輪郭線を表示/非表示
[q]キー:頂点1を表示/非表示
[w]キー:頂点2を表示/非表示
[e]キー:頂点3を表示/非表示
[r]キー:頂点4を表示/非表示

やっと2つめのカメラを購入したので、さっそくTwoViewを触ってみました。
遅くなってしまったので、コメントを頂いた方はもう解決したかもしれませんが、一応書いておきます。

基本的には、工学ナビ様のBBSで説明されている手順通りです。
詳しく書いてみます。

ちなみに僕の環境は以下の通りです。
OS      : Microsoft Windows XP Home Edition (SP2)
CPU    : AMD Athlon^(TM) 64 X2 プロセッサ 3800+
メモリ   : DDR SDRAM 1GB
Webカメラ:Qcam Pro for Notebooks ×2


1.GraphEditの入手
GraphEditは、DirectX SDKの中に入っています。
ひとまず、"C:\Program Files\"内で、GraphEditとか、graphedtで検索してみてください。
見つからない場合は、DirectX SDKをインストールしてください。


2.Webカメラのデバイス名を取得
GraphEditを起動したら、メニューから[Graph]->[Insert Filters...]を選択、ダイアログを開きます。
ダイアログのツリーから[Video Capture Source]を開き、その中の各Webカメラ名を選択します。
僕の場合は、[Qcam Pro for Notebooks]が2つあります。

各Webカメラ名を選択したときに、一番下のエディットボックスに表示される長い文字列が、そのWebカメラのデバイス名になります。僕の場合は、

@device:pnp:\\?\usb#vid_046d&pid_0991&mi_00
#6&22a7aaac&0&0000#{65e8773d-8f56-11d0-a3b9-00a0c9223196}\
{bbefb6c7-2fc4-4139-bb8b-a58bba724083}

@device:pnp:\\?\usb#vid_046d&pid_0991&mi_00
#6&a9d31db&1&0000#{65e8773d-8f56-11d0-a3b9-00a0c9223196}\
{bbefb6c7-2fc4-4139-bb8b-a58bba724083}
という文字列でした。

この文字列は、もちらんそのカメラ固有のもので、僕のカメラのものを他の人が使っても上手くいきません。


3.ARToolKitの設定ファイルを書き換える
Dataフォルダの"WDM_camera_flipV.xml"というファイルを、適当なエディタで開きます。
その中で、

<camera show_format_dialog="true" friendly_name="PGR">

という行があるはずです。これに"device_name"追加して、以下のようにします。

<camera show_format_dialog="true" friendly_name="PGR" device_name="">

ここで、先ほど取得したカメラのデバイス名を、device_nameの""内にコピーします。

<camera show_format_dialog="true" friendly_name="PGR" device_name="@device:pnp:\\?\usb#vid_046d&pid_0991&mi_00#
6&22a7aaac&0&0000#{65e8773d-8f56-11d0-a3b9-00a0c9223196}\
{bbefb6c7-2fc4-4139-bb8b-a58bba724083}">

次に、コピーした文字列で"&"になっている部分を"&amp;"に置き換えます。

<camera show_format_dialog="true" friendly_name="PGR" device_name="@device:pnp:\\?\usb#vid_046d&amp;pid_0991&amp;mi_00#
6&amp;22a7aaac&amp;0&amp;0000#{65e8773d-8f56-11d0-a3b9-00a0c9223196}\
{bbefb6c7-2fc4-4139-bb8b-a58bba724083}">

この変更したファイルを、別名で保存します。
ここでは、"WDM_camera_flipV_01.xml"とします。

もう1つのデバイス名についても同じ作業をして、名前を変えて保存します。
こっちは、"WDM_camera_flipV_02.xml"としました。


4.プログラムを書き換える
プロジェクトを開いて、twoView.cを変更します。
"WDM_camera_flipV.xml"のところを、新しく作ったファイルに置き換えます。

#if defined(_WIN32)
        "Data\\WDM_camera_flipV.xml",
        "Data\\WDM_camera_flipV.xml",
#elif defined(__APPLE__)



#if defined(_WIN32)
        "Data\\WDM_camera_flipV_01.xml",
        "Data\\WDM_camera_flipV_02.xml",
#elif defined(__APPLE__)
に変更しました。



以上で終わりです。
僕の場合は、これでうまくいきました。

これでも失敗する場合は、残念ながら僕には分かりません。
twoViewについては、他にも解説しているサイトがあるので、そちらを参照してください。

Webカメラの種類も関係あるみたいです。




※追記 

○08/05/09

一方のカメラのフレームレートだけ、やけに遅い問題が発生していましたが、
"videoWin32DirectShow.cpp"ファイルの

const long                frame_timeout_ms = 0L;
というのを

const long                frame_timeout_ms = 100L;
ぐらいにしたら、直りました。数値は適当です。


○08/05/10

一方のカメラの画面が上下反転していた問題がありました。
調べてみると、"WDM_camera_flipV_02.xml"ファイルだけ、

<RGB32 flip_h="false" flip_v="true"/>


<RGB32/>
となっていたのが原因でした。
このファイルを作るのに、"WDM_camera.xml"を利用したのが間違いだったみたいです。

解析の2回目は、マーカーの認識についてです。
前回の変換行列の計算で、省略した部分を説明します。



---- < マーカー認識の概要 > ----


※注意
この動画で、画像の色分けでは、そのつど色が計算されるため、多くの色が点滅するような動画になっています。
また、画像の生成等の重い処理を行っているので、フレームレートは低くなっています。

光過敏性発作などがある方は、予めご注意ください。





BGM:エコテロニカ(sansuiさん)

なんだかムーンサイドみたいですね。
ちょっと誤字があるようですが、気にしないでください。


ARToolKitでは、変換行列を計算する前に、その基となるマーカーを知っていなければなりません。
カメラに映った画像から、どこにマーカーがあるのか判定するために、おおまかに以下のような処理を行います。


1.カメラからの画像を2値化して、画像の暗い部分を探す
2.暗い部分で、それぞれの閉じた領域に印を付ける
3.各閉領域で、一番端の点を最初の頂点(頂点1)とする
4.頂点1から、領域の輪郭を探す
5.輪郭が分かったら、2番目の頂点を探す
6.残りの頂点3,4を探す
7.4つの頂点をもつ領域を4角形と判断する
8.4角形内の画像を単純化する
9.単純化された画像に対して、パターンマッチングを行う

それぞれの処理を見てみます。



---- < 1.カメラからの画像を2値化して、画像の暗い部分を探す > ----

カメラから得られたカラー画像を、2値化して白or黒のみの画像にします。
ARToolKitでは、黒枠のマーカーを認識するので、
ここでの画像は白黒を反転したようなものになっています。



---- < 2.暗い部分で、それぞれの閉じた領域に印を付ける > ----

画像を2値化すると、閉じた領域がたくさんできます。
それぞれの閉領域を区別するために、ラベリングと呼ばれる作業を行います。

これは、各領域を走査して、繋がっている部分に同じ番号を割り振っていく作業になります。



---- < 3.各閉領域で、一番端の点を最初の頂点(頂点1)とする > ----

2で閉領域が見つかったので、各閉領域の外接矩形を求めます。

そして、外接矩形の辺上にある点を探します。
これが、最初の頂点(頂点1)になります。



---- < 4.頂点1から、領域の輪郭を探す > ----

次に、輪郭を探します。
頂点1からスタートして、閉領域の輪郭を辿るように、点を見つけていきます。
最後に、頂点1に戻ってきます。



---- < 5.輪郭が分かったら、2番目の頂点を探す > ----

輪郭は点の集まりなので、その中で頂点1から最も離れたものを探します。
それを頂点2とします。



---- < 6.残りの頂点3,4を探す > ----

頂点1、2から頂点3,4を探します。
まず輪郭点を、"頂点1→2" と "頂点2→1" の2つに分けます。

2つの輪郭点で、頂点1・2の線分から、最も離れた点を探します。
これを再帰的に繰り返して、残りの頂点も見つけ出します。



---- < 7.4つの頂点をもつ領域を4角形と判断する > ----

上記の処理の途中で、領域が望みの4角形かどうかが、だいたい判定されます。

最初に、面積が非常に小さい、または大きい領域は除外されます。
他にも、4つの頂点がうまく見つからない領域も除外されます。

4つの頂点は、順番に並んでいるとは限らないので、
そのつど調整されます。



---- < 8.4角形内の画像を単純化する > ----

4角形が見つかったので、やっとパターンの判別に入ります。
判別のために、4角形内の画像は単純かされます。

マーカーが斜めに映っている場合も、内部の画像は修正され、真正面からのものになり、
解像度も非常に小さくなります。



---- <
9.単純化された画像に対して、パターンマッチングを行う > ----

この単純化された画像に対して、パターンマッチングを行います。
パターンマッチングでは、基となるパターンと単純化された4角形内の画像を比較し、誤差を計算します。

この誤差が、ある決まった値より小さい場合に、これが求めるマーカーであると判定されることになります。
(実際には、最も誤差が小さいパターンが、その4角形に割り当てられ、後の判断はプログラマに委ねられます。



---- < 最後に > ----

調べてみて分かりましたが、すばらしいアルゴリズムですね。
非常に勉強になりました。

やはり、所々怪しいところはありますので、間違い等はご指摘いただければありがたいです。



あまり関係ないですが、BGMを使用させてもらっているsansuiさんつながりで、「ゆめにっき」というフリーゲームを現在やっています。
RPGツクールで作られた、夢の中を探索するという戦闘のないゲームなんですが、「とても怖いゲーム」です。
まるで、マザー2のムーンサイドのような怖さで、今回作った動画もプログラムもそれっぽいと思ってしまいました。



安定化の次は、プログラムの高速化などをやってみようと思ってましたが、
高速化のためには、ARToolKitのソースをもっと理解していなければなりません。

そこで、次に「ARToolKitの解析」を行うことにしました。


解析のその1は、変換行列の計算アルゴリズムの解説をやってみます。
ここでいう変換行列とは、マーカー(オブジェクト)座標系から、グローバル(カメラ)座標系へ変換する行列のことです。

これは、ARToolKitの重要な仕組みの1つです。



---- < 変換行列計算の概要 > ----



BGM:エコテロニカ(sansuiさん)

 OpenGLなどの3DCGでは、3Dモデルの移動・回転などを行うために、座標系の変換行列を用います。
ARToolKitの場合も、この変換行列がないと、マーカー上にモデルを表示できません。

プログラムでは、スクリーン上に映ったマーカーの画像から、この変換行列を計算しています。
計算のプロセスは、大まかに以下のようになります。

1.マーカーの黒い四角形を認識する
2.四角形の枠である、4辺を計算する
3.それぞれの辺を、カメラの投影方向に伸ばし、面を作る
4.面の法線を計算
5.向かい合った面同士の法線から、外積を計算する
6.その外積同士から、さらに外積を計算
7.現在わかっている式を用いて、行列の残り値を計算する


今回は"スクリーン上の4角形の4辺"から、"変換行列の3×3の要素"を計算するとこまでを中心に、解説していきます。



---- < 1.マーカーの黒い四角形を認識 > ----

四角形の認識ではまず、カメラに映った画像を2値化(白黒に)します。
そして、その白黒画像から、4角形を探します。
さらに、見つけた4角形が登録されているマーカーかどうか調べます。

この部分の詳しいアルゴリズムについては、後日解説するつもりです。



---- < 2.四角形の枠である、4辺を計算する > ----

ここも、1と一緒に解説いたします。



---- < 3.それぞれの辺を、カメラの投影方向に伸ばし、面を作る > ----

4角形の辺が取得できたら、この辺を伸ばして、面にします。
伸ばし方は、辺がスクリーンへ投影された方向に伸ばします。

つまりこの面は、3次元空間上のマーカーの4辺も通ることになります。
カメラの視点、2Dのマーカーの辺、3Dのマーカーの辺を繋ぐ面です。



---- < 4.面の法線を計算 > ----

それぞれの面の法線(面に対して、垂直なベクトル)を計算します。



---- < 5.向かい合った面同士の法線から、外積を計算する > ----

法線が計算できたら、向かい合った面同士の法線から、外積を計算します。
外積は、2つの法線に対し垂直になります。

この外積は、マーカー座標系の1つの軸を表すベクトルになります。



---- < 6.その外積同士から、さらに外積を計算 > ----

さらに、外積同士の外積をとります。
この3つの外積が、それぞれマーカー座標系のX軸, Y軸, Z軸になります。

さらに値を補正すると、変換行列の3×3の要素になるわけです。



---- < 8.現在わかっている式を用いて、行列の残り値を計算する > ----

変換行列の3×3の要素は、回転や拡大縮小を表しています。
変換行列に必要な残りの要素は、平行移動です。

平行移動分を計算するには、それまでに用いられた行列等の式を使い、答えを出します。



以上が、僕が理解できた範囲でのアルゴリズムの説明です。
間違いや不足があった場合には、連絡をお願いします。