最近はARToolKitのプログラムも少しできてきたので、
次は手とかをそのまま認識させたいと思っていました。
そしたらちょうどmasafumiさんという方のブログで手認識を使ったARという記事を見つけました。
この方法は認識精度が高そうなので、twoViewでの物体認識にも使えそうです。
この手認識ARの研究は、手を認識することによってマーカーの代わりにしようというものです。
ソースも公開されているので、すぐにこのプログラムを使った動画がアップされています。
見ての通り、指認識の精度は非常に高いです。
というわけで今回は、この指認識のアルゴリズムだけをいただき、
この情報を元にtweVIewから指の3次元座標を計算してみました。
これで綿棒や色の付いた目印を使う必要がなくなります。
--- < 指認識の概要 > ---
指認識では、おおまかに以下のような処理を行っています。
1.手の色に近い領域を探し、輪郭線で分ける
2.距離変換画像を作成する
3.輪郭線を使ったラベリング
4.手の輪郭のカーブ具合を調べる
5.カーブ具合から指の判定
僕が感動したのは、手の輪郭線から指を判定する処理です。
--- < 1.手の色に近い領域を探し、輪郭線で分ける > ---
やはり最初は手の色を判定し、それを元に2値画像を作成します。
元のプログラムではここでRGBをYCrCbという色空間に変換し、
ヒストグラムなどを使って肌色を判定していました。
ですが、僕のプログラムでは前回のHSV色空間を使用しました。
そしてさらに、この肌色の2値画像から輪郭線情報を作成します。
輪郭線はOpenCVのcvFindContoursを使用して作成します。
--- < 2.距離変換画像を作成する > ---
次に、2値化画像から距離変換画像を作成します。
距離変換とは、値のある現在の点から値のない点への最短距離を求める計算です。
この最短距離を画像に書き込むと、上の1つめの画像のようになります。
この距離変換によって、肌色に近い領域で一番大きなものが大体分かります。
今回は、「その一番大きな領域が手である」という判定をしています。
--- < 3.輪郭線を使ったラベリング > ---
この手の領域を判別するために、輪郭線を使用します。
1.まっさらな画像を用意
2.画像に対し、各輪郭ごとに内部を塗りつぶしていく
3.塗りつぶすたびに、「距離変換の値が最大となる座標」が塗り潰されたかどうか判定する。
4.もし塗りつぶされいれば、その輪郭が「求める手の領域」のものになる。
この処理によってラベリングを行わずに、最大面積の領域を判定できてしまいます。
ラベリング処理は重いものが多いので、これはとても便利です。
結果的に上の2つ目の画像のようなものができあがります。
--- < 4.手の輪郭のカーブ具合を調べる > ---
手の輪郭を取得できたので、次にこれを用いて指を判定します。
指判定には、各輪郭点の狭い範囲で内積と外積を計算します。
1.輪郭線上の全ての点を調べる
2.各点の狭い範囲で、先の輪郭点へのベクトルと、後の輪郭点へのベクトルを取得する。
3.2つのベクトルの内積を計算し、それがある程度大きければ、その点はカーブしていると判定
4.2つのベクトルの外積を計算し、そのカーブの方向を判定
5.調べる範囲を変えて、2~4の処理を繰り貸す。
6.求める値になったら、そのカーブが指に似た曲がり具合であると判断する。
この処理で、上の3つ目の画像ができあがります。
--- < 5.カーブ具合から指の判定 > ---
上記の処理で指っぽいカーブは分かりましたが、これが本当に指かどうかはまだ分かりません。
また、場合によってはその指がどの指なのかも判定が必要です。
ですが、今回のプログラムではその処理を入れていません。
ここでは最後に、前回同様ステレオでこの2次元座標を3次元に変換して終わりです。
これで上の最後の画像のようになります。
元の研究では、事前に指の理想的な位置を取得しておいて、
それと比較することで各指を認識したりしているようです。
僕のプログラムも指っぽい形状の認識の精度は高くなりましたが、
その後の指判定はまだできていません。
ソースの清書も含め、その辺が今後の課題となります。
次は手とかをそのまま認識させたいと思っていました。
そしたらちょうどmasafumiさんという方のブログで手認識を使ったARという記事を見つけました。
この方法は認識精度が高そうなので、twoViewでの物体認識にも使えそうです。
この手認識ARの研究は、手を認識することによってマーカーの代わりにしようというものです。
ソースも公開されているので、すぐにこのプログラムを使った動画がアップされています。
見ての通り、指認識の精度は非常に高いです。
というわけで今回は、この指認識のアルゴリズムだけをいただき、
この情報を元にtweVIewから指の3次元座標を計算してみました。
これで綿棒や色の付いた目印を使う必要がなくなります。
--- < 指認識の概要 > ---
指認識では、おおまかに以下のような処理を行っています。
1.手の色に近い領域を探し、輪郭線で分ける
2.距離変換画像を作成する
3.輪郭線を使ったラベリング
4.手の輪郭のカーブ具合を調べる
5.カーブ具合から指の判定
僕が感動したのは、手の輪郭線から指を判定する処理です。
--- < 1.手の色に近い領域を探し、輪郭線で分ける > ---
やはり最初は手の色を判定し、それを元に2値画像を作成します。
元のプログラムではここでRGBをYCrCbという色空間に変換し、
ヒストグラムなどを使って肌色を判定していました。
ですが、僕のプログラムでは前回のHSV色空間を使用しました。
そしてさらに、この肌色の2値画像から輪郭線情報を作成します。
輪郭線はOpenCVのcvFindContoursを使用して作成します。
--- < 2.距離変換画像を作成する > ---
次に、2値化画像から距離変換画像を作成します。
距離変換とは、値のある現在の点から値のない点への最短距離を求める計算です。
この最短距離を画像に書き込むと、上の1つめの画像のようになります。
この距離変換によって、肌色に近い領域で一番大きなものが大体分かります。
今回は、「その一番大きな領域が手である」という判定をしています。
--- < 3.輪郭線を使ったラベリング > ---
この手の領域を判別するために、輪郭線を使用します。
1.まっさらな画像を用意
2.画像に対し、各輪郭ごとに内部を塗りつぶしていく
3.塗りつぶすたびに、「距離変換の値が最大となる座標」が塗り潰されたかどうか判定する。
4.もし塗りつぶされいれば、その輪郭が「求める手の領域」のものになる。
この処理によってラベリングを行わずに、最大面積の領域を判定できてしまいます。
ラベリング処理は重いものが多いので、これはとても便利です。
結果的に上の2つ目の画像のようなものができあがります。
--- < 4.手の輪郭のカーブ具合を調べる > ---
手の輪郭を取得できたので、次にこれを用いて指を判定します。
指判定には、各輪郭点の狭い範囲で内積と外積を計算します。
1.輪郭線上の全ての点を調べる
2.各点の狭い範囲で、先の輪郭点へのベクトルと、後の輪郭点へのベクトルを取得する。
3.2つのベクトルの内積を計算し、それがある程度大きければ、その点はカーブしていると判定
4.2つのベクトルの外積を計算し、そのカーブの方向を判定
5.調べる範囲を変えて、2~4の処理を繰り貸す。
6.求める値になったら、そのカーブが指に似た曲がり具合であると判断する。
この処理で、上の3つ目の画像ができあがります。
--- < 5.カーブ具合から指の判定 > ---
上記の処理で指っぽいカーブは分かりましたが、これが本当に指かどうかはまだ分かりません。
また、場合によってはその指がどの指なのかも判定が必要です。
ですが、今回のプログラムではその処理を入れていません。
ここでは最後に、前回同様ステレオでこの2次元座標を3次元に変換して終わりです。
これで上の最後の画像のようになります。
元の研究では、事前に指の理想的な位置を取得しておいて、
それと比較することで各指を認識したりしているようです。
僕のプログラムも指っぽい形状の認識の精度は高くなりましたが、
その後の指判定はまだできていません。
ソースの清書も含め、その辺が今後の課題となります。
新しいプログラムは「物体認識」とか凄そうな名前を付けましたが、
基本的にはtwoViewを使ったプログラムの延長です。
今回は、前回のプログラムの色認識を改良しました。
少しずつ純粋なARToolKitから離れてきています。
--- < HSV色空間について > ---
HSVなんて聞きなれない単語ですが、簡単に言うとRGBの親戚みたいなものです。
知っての通り、RGBは「赤・緑・青」の色を組み合わせて多くの色を表現しますが、
HSVでは代わりに「色相・彩度・明度」という属性を組み合わせることで色を作ります。
詳しくは、Wikiを参照してください。
なぜRGBじゃなくてHSVを使うのかというと、こっちの方が特定の色を見つけやすいからです。
Wikiを見てもらえば分かるように、HSVでは赤とか青とかの情報は色相を見れば大体分かります。
残りの彩度や明度は、言ってみれば明るさや暗さみたいなもので、ほとんど無視していいものです。
逆に言えば「明るい赤」も「暗い赤」も、同じ赤として認識してくれることになります。
実際には、物体の「影の部分」や「光が反射している部分」まで認識してくれる可能性がある、
ということです。
--- < 実行結果 > ---
今回は、このHSV色空間で画像を扱うために、OpenCVというフリーの画像処理ライブラリを使用しました。
また指定色の物体は綿棒ではなく、色風船の端を切って指にはめてみました。
HSV色系を用いる以外の処理は、今までと一緒です。
とりあえずはうまくいっているようです。
しかし、欠点もあります。
HSV色系はそのままだと感度が良すぎて、余計なものまで認識してしまいます。
上の画像では、閾値を狭くしたので指の先を正しく認識できていません。
そこで、閾値を広くすると下の画像のようになります。
閾値が広いと指の部分を正しく取得できますが、同時に関係ない部分まで誤認してしまっています。
このような結果にならないように、「彩度・明度」も考量しなくてはならず、
「色相」の閾値も、そのつど微調整が必要です。
またこれまで以上に、撮影環境に気を使わなくてはいけないかもしれません。
この辺は今後の課題です。
基本的にはtwoViewを使ったプログラムの延長です。
今回は、前回のプログラムの色認識を改良しました。
少しずつ純粋なARToolKitから離れてきています。
--- < HSV色空間について > ---
HSVなんて聞きなれない単語ですが、簡単に言うとRGBの親戚みたいなものです。
知っての通り、RGBは「赤・緑・青」の色を組み合わせて多くの色を表現しますが、
HSVでは代わりに「色相・彩度・明度」という属性を組み合わせることで色を作ります。
詳しくは、Wikiを参照してください。
なぜRGBじゃなくてHSVを使うのかというと、こっちの方が特定の色を見つけやすいからです。
Wikiを見てもらえば分かるように、HSVでは赤とか青とかの情報は色相を見れば大体分かります。
残りの彩度や明度は、言ってみれば明るさや暗さみたいなもので、ほとんど無視していいものです。
逆に言えば「明るい赤」も「暗い赤」も、同じ赤として認識してくれることになります。
実際には、物体の「影の部分」や「光が反射している部分」まで認識してくれる可能性がある、
ということです。
--- < 実行結果 > ---
今回は、このHSV色空間で画像を扱うために、OpenCVというフリーの画像処理ライブラリを使用しました。
また指定色の物体は綿棒ではなく、色風船の端を切って指にはめてみました。
HSV色系を用いる以外の処理は、今までと一緒です。
とりあえずはうまくいっているようです。
しかし、欠点もあります。
HSV色系はそのままだと感度が良すぎて、余計なものまで認識してしまいます。
上の画像では、閾値を狭くしたので指の先を正しく認識できていません。
そこで、閾値を広くすると下の画像のようになります。
閾値が広いと指の部分を正しく取得できますが、同時に関係ない部分まで誤認してしまっています。
このような結果にならないように、「彩度・明度」も考量しなくてはならず、
「色相」の閾値も、そのつど微調整が必要です。
またこれまで以上に、撮影環境に気を使わなくてはいけないかもしれません。
この辺は今後の課題です。
お待たせしました。
twoViewを使った、ステレオマッチングプログラムのソース公開です。
StereoAnyColor1.0.zip
--- < プログラムの使い方 > ---
0.Webカメラが2つあることが最低条件です。
ちなみに僕のは"Qcam Pro for Notebooks"。他のカメラでは試していないので、動くかどうかはわからないです。
あと、2つのカメラの設定を同じにしてください。
1.2つのカメラの向きを平行にして、まっすぐに並べる。
2.赤・黄・青など特徴的な色の物体を用意し、場所は白いテーブルの上など選んだ色が他にはないような所にする。
3.TwoViewに関する説明を読み、"WDM_camera_flipV_01.xml"、"WDM_camera_flipV_02.xml"を作成。
4.プログラムを起動して"Video Source 1"ウィンドウで、用意した特長的な色の物体を画面に表示して、それを左クリックします。
5.物体が画面に映っている状態で、Fキーを押してラベリング画像を表示。
6.Kキーを押して続けて閾値を下げ、目的の物体が一番大きくラベリングされるようにします。
7.押しすぎて全くラベリングされなくなったら、Jキーを押して閾値を上げます。
8.もう1つの物体を認識させたいときは、右クリックで画面上の物体の色を指定します。
9.2つの物体が一番大きくラベリングされるようになったらOKです、Fキーを押して元の画面に戻ります。
10.マーカーを表示し、マーカーと2つの物体の距離が表示されることを確認します。
11.2つの物体の距離を近づけて、カーソルがクリック状態になるかどうか(2つの物体間の線が赤くなる)を確認します。
12.マーカー上の立方体にカーソルを重ねて、クリック状態にします。
13.クリック状態でカーソルを移動すると、立方体も移動します。
14.立方体の移動中にクリックを解除すると、立方体の移動が止まります。
以上です。
まだ色の識別が甘いので、上手く認識しないかもしれません。
今後の課題です。
--- < ソースの概要 > ---
今回のプログラムでは、いくつかのクラスを追加しました。
・CCamerasRelationクラス:カメラ間の関係
・CStereoAnyColorPointクラス:指定色のステレオ処理を行う
・CFindColorPosInImgクラス:画像認識を行い、指定色のスクリーン上の座標等を計算
・CCamSimpleTriangulationクラス:3角測量を行う
以上のクラスは次のように使います。
1.インスタンスの定義
2.クラスの初期化
3.画像認識の計算
4.結果の取得
--- < 1.インスタンスの定義 > ---
改良したtwoView.cでは、以下のようにクラスのインスタンスを作成します。
--- < 2.クラスの初期化 > ---
次に、この変数を初期化します。
CCamerasRelationの初期化では、SetCamParamで各カメラに透視投影行列を設定します。
マーカーを認識できた時点で、もう1度別の初期化を行います
CCamerasRelationで、カメラ間の関係を計算します
--- < 3.画像認識の計算 > ---
必要な情報をセットできたら、画像処理を行います。
--- < 4.結果の取得 > ---
計算が上手くいったら、結果の3次元ベクトルを取得します
twoViewを使った、ステレオマッチングプログラムのソース公開です。
StereoAnyColor1.0.zip
--- < プログラムの使い方 > ---
0.Webカメラが2つあることが最低条件です。
ちなみに僕のは"Qcam Pro for Notebooks"。他のカメラでは試していないので、動くかどうかはわからないです。
あと、2つのカメラの設定を同じにしてください。
1.2つのカメラの向きを平行にして、まっすぐに並べる。
2.赤・黄・青など特徴的な色の物体を用意し、場所は白いテーブルの上など選んだ色が他にはないような所にする。
3.TwoViewに関する説明を読み、"WDM_camera_flipV_01.xml"、"WDM_camera_flipV_02.xml"を作成。
4.プログラムを起動して"Video Source 1"ウィンドウで、用意した特長的な色の物体を画面に表示して、それを左クリックします。
5.物体が画面に映っている状態で、Fキーを押してラベリング画像を表示。
6.Kキーを押して続けて閾値を下げ、目的の物体が一番大きくラベリングされるようにします。
7.押しすぎて全くラベリングされなくなったら、Jキーを押して閾値を上げます。
8.もう1つの物体を認識させたいときは、右クリックで画面上の物体の色を指定します。
9.2つの物体が一番大きくラベリングされるようになったらOKです、Fキーを押して元の画面に戻ります。
10.マーカーを表示し、マーカーと2つの物体の距離が表示されることを確認します。
11.2つの物体の距離を近づけて、カーソルがクリック状態になるかどうか(2つの物体間の線が赤くなる)を確認します。
12.マーカー上の立方体にカーソルを重ねて、クリック状態にします。
13.クリック状態でカーソルを移動すると、立方体も移動します。
14.立方体の移動中にクリックを解除すると、立方体の移動が止まります。
以上です。
まだ色の識別が甘いので、上手く認識しないかもしれません。
今後の課題です。
--- < ソースの概要 > ---
今回のプログラムでは、いくつかのクラスを追加しました。
・CCamerasRelationクラス:カメラ間の関係
・CStereoAnyColorPointクラス:指定色のステレオ処理を行う
・CFindColorPosInImgクラス:画像認識を行い、指定色のスクリーン上の座標等を計算
・CCamSimpleTriangulationクラス:3角測量を行う
以上のクラスは次のように使います。
1.インスタンスの定義
2.クラスの初期化
3.画像認識の計算
4.結果の取得
--- < 1.インスタンスの定義 > ---
改良したtwoView.cでは、以下のようにクラスのインスタンスを作成します。
CCamerasRelation gCamRelate(CONTEXTSACTIVECOUNT);今回はグローバル変数で定義しました。
CStereoAnyColorPoint g_stereoOperator;
--- < 2.クラスの初期化 > ---
次に、この変数を初期化します。
// グローバル変数の初期化CStereoAnyColorPointの初期化では、指定色をAddTargetColorで追加します。
static void InitGlobalValue()
{
// 画像認識の色を指定
g_stereoOperator.ClearTargetColor();
g_stereoOperator.AddTargetColor( g_cnColorRealRed );
g_stereoOperator.AddTargetColor( g_cnColorRealYellow );
// カメラの投影行列
gCamRelate.SetCamParam( 0, arParam.mat );
gCamRelate.SetCamParam( 1, arParam.mat );
}
CCamerasRelationの初期化では、SetCamParamで各カメラに透視投影行列を設定します。
マーカーを認識できた時点で、もう1度別の初期化を行います
CCamerasRelationで、カメラ間の関係を計算します
// 2つのカメラが、同じマーカーを認識できてるかどうかCStereoAnyColorPointでは、新しいカメラ画像をセット
if ( gContextsActive[0].id == gContextsActive[1].id )
{
// カメラ1の変換行列を保存
CopyArray3x4ToMatrix4x4( mtrxTemp1, gContextsActive[0].patt_trans );
CopyArray3x4ToMatrix4x4( mtrxTemp2, gContextsActive[1].patt_trans );
gCamRelate.CalcRelation(0, 1, mtrxTemp1, mtrxTemp2 );
}
g_stereoOperator.InitImgSource( arImXsize, arImYsize,
gContextsActive[0].ARTImage,
gContextsActive[1].ARTImage );
--- < 3.画像認識の計算 > ---
必要な情報をセットできたら、画像処理を行います。
g_stereoOperator.RecognitionColor3DPos(&gCamRelate);画像処理では、カメラの関係を引数として指定します。
--- < 4.結果の取得 > ---
計算が上手くいったら、結果の3次元ベクトルを取得します
for ( int iTarget = 0 ; iTarget < g_stereoOperator.GetTargetPosCount() ; iTarget++ )GetTargetPos(const int ciTarget, double dPos[3])で、指定色のベクトルを取得できます。
{
double dPos[3];
if ( !g_stereoOperator.GetTargetPos(iTarget, dPos) )
continue;
}
twoViewを使ったプログラム、4つ目はポインティングデバイスです。
これは、前回の3つのプログラムの機能を合わせて作成しています。
------- < 機能の概要 > -------
以下、プログラムの処理です。
1.最初にカメラ間の関係を表す、変換行列を計算しておく
2.次に、特徴的な色の検出を行う
3.2の結果から、その色の物体の位置を検出
4.2つの物体の位置を元に、ポインティングを行う
1~3までは、これまでのtwoViewを用いたプログラムに対応しています。
------- < 4.2つの物体の位置を元に、ポインティングを行う > -------
ポインティングは次のような手順です
1.2つの目印の距離を測る
2.距離が短いときは、クリックしていると判定
3.クリック中に、3Dモデルとの衝突判定を行う
4.衝突していたら、モデルをカーソルと一緒に移動する
5.距離が離れてクリックが解除されたら、モデルの移動も止める
------- < 参考にした作品 > -------
③にも書きましたが、色認識を用いて物体の位置を求めるアイデアは、こちらの動画からいただきました。
求めた物体の座標をポインティングデバイスとして使用するアイデアは、この動画からのものです。
お二人には、この場を借りて感謝いたします。
また、他の開発者の皆さんからも、たくさんアイデアを頂いています。
良い作品を見せていただき、ありがとうございました。
これからも、よろしくお願いします。
これは、前回の3つのプログラムの機能を合わせて作成しています。
------- < 機能の概要 > -------
以下、プログラムの処理です。
1.最初にカメラ間の関係を表す、変換行列を計算しておく
2.次に、特徴的な色の検出を行う
3.2の結果から、その色の物体の位置を検出
4.2つの物体の位置を元に、ポインティングを行う
1~3までは、これまでのtwoViewを用いたプログラムに対応しています。
------- < 4.2つの物体の位置を元に、ポインティングを行う > -------
ポインティングは次のような手順です
1.2つの目印の距離を測る
2.距離が短いときは、クリックしていると判定
3.クリック中に、3Dモデルとの衝突判定を行う
4.衝突していたら、モデルをカーソルと一緒に移動する
5.距離が離れてクリックが解除されたら、モデルの移動も止める
------- < 参考にした作品 > -------
③にも書きましたが、色認識を用いて物体の位置を求めるアイデアは、こちらの動画からいただきました。
求めた物体の座標をポインティングデバイスとして使用するアイデアは、この動画からのものです。
お二人には、この場を借りて感謝いたします。
また、他の開発者の皆さんからも、たくさんアイデアを頂いています。
良い作品を見せていただき、ありがとうございました。
これからも、よろしくお願いします。
長い時間上手くいかず悩んでいましたが、やっとそれらしい結果が出るようになりました。
前回の特徴物体の位置から、物体までの距離を求めます。
参考にしたのは、以下の動画です。
この方は、他にもARToolKitと物理演算を合わせたりと、かなり高度なことをやっています。
今回は、簡単な三角測量を用いて距離計算を行っています。
簡単なケースでは、2つのカメラの向きを平行にして、Y・Z座標の位置も同じにします。
ちなみに失敗していた理由は、VIEW_SCALEFACTORという値を考えていなかったからでした。
これは、ARToolKitの長さの単位から、OpenGLの単位へ変換するときに必要になります。
まだ誤差などがあるようなので、これから調整します。
--------- < 追記 > ---------
○08/05/29
誤差が安定してきたので、処理を大まかに説明。
前回(その②)の各カメラでの、特徴点のスクリーン座標系の位置を取得。
これを、透視投影変換の逆行列を使って、カメラ座標系の位置に変換。
また、その①で求めたカメラ間の変換行列から、カメラのX座標系での距離を取得しておく。
そこで、以下の計算を行う。
1.a = カメラ間のXの距離 / (カメラ1での特徴点のX座標 - カメラ2での特徴点のX座標)
2.カメラ1の特徴点の座標(カメラ座標系)をa倍する。
3.2の結果が、特徴点で示した物体の3次元空間上での座標(カメラ座標系)になる。
4.カメラ1で、マーカーからの変換行列の逆行列を計算しておく
5.2の結果に対し、4の逆行列をかけて、マーカー座標系での特徴物体の座標を計算する。
6.5に対し、VIEW_SCALEFACTORをかけて、OpenGLの長さの単位にする。
このようにして、出た座標を表示すると、最初のような画像になります。
画像では、[マーカー座標系の原点]から[特徴物体の真下]までの線と、そこから[特徴物体]までの線を描画しています。
○08/05/30
まだ微妙な誤差がありましたが、三角測量の倍数の計算で、
X値だけじゃなくY値も入れた距離で計算したら上手くいきました。
前回の特徴物体の位置から、物体までの距離を求めます。
参考にしたのは、以下の動画です。
この方は、他にもARToolKitと物理演算を合わせたりと、かなり高度なことをやっています。
今回は、簡単な三角測量を用いて距離計算を行っています。
簡単なケースでは、2つのカメラの向きを平行にして、Y・Z座標の位置も同じにします。
ちなみに失敗していた理由は、VIEW_SCALEFACTORという値を考えていなかったからでした。
これは、ARToolKitの長さの単位から、OpenGLの単位へ変換するときに必要になります。
まだ誤差などがあるようなので、これから調整します。
--------- < 追記 > ---------
○08/05/29
誤差が安定してきたので、処理を大まかに説明。
前回(その②)の各カメラでの、特徴点のスクリーン座標系の位置を取得。
これを、透視投影変換の逆行列を使って、カメラ座標系の位置に変換。
また、その①で求めたカメラ間の変換行列から、カメラのX座標系での距離を取得しておく。
そこで、以下の計算を行う。
1.a = カメラ間のXの距離 / (カメラ1での特徴点のX座標 - カメラ2での特徴点のX座標)
2.カメラ1の特徴点の座標(カメラ座標系)をa倍する。
3.2の結果が、特徴点で示した物体の3次元空間上での座標(カメラ座標系)になる。
4.カメラ1で、マーカーからの変換行列の逆行列を計算しておく
5.2の結果に対し、4の逆行列をかけて、マーカー座標系での特徴物体の座標を計算する。
6.5に対し、VIEW_SCALEFACTORをかけて、OpenGLの長さの単位にする。
このようにして、出た座標を表示すると、最初のような画像になります。
画像では、[マーカー座標系の原点]から[特徴物体の真下]までの線と、そこから[特徴物体]までの線を描画しています。
○08/05/30
まだ微妙な誤差がありましたが、三角測量の倍数の計算で、
X値だけじゃなくY値も入れた距離で計算したら上手くいきました。
日々ARToolKitに関するニュースを探しているPipeRですが、
先日ARToolKitをFlashに移植されたというブログを見つけました。
FlashのActionScriptは昔少しだけ触ったことがありますが、
これでARToolKitを作ってしまえるとは驚きました。
ARToolKitをJAVAに移植したA虎@さんのNyARToolkitといい、
スキルの高い開発者の方々が、ARToolKitのさらなるマルチプラットフォーム化を進めています。
これは、より多くの人がARToolKitを使えるようになる、すばらしいことです。
私の開発の目的はあくまで自分の勉強のためですが、
このような方々のように、もっと有用なプログラムを作りたいですね。
あと有用なプログラムといえば、ギズモード・ジャパンで紹介されていた、
ARToolKitを使ったゲームなんてのもあります。
これも非常に面白くて、ARToolKitを上手く生かしてると思いました。
先日ARToolKitをFlashに移植されたというブログを見つけました。
FlashのActionScriptは昔少しだけ触ったことがありますが、
これでARToolKitを作ってしまえるとは驚きました。
ARToolKitをJAVAに移植したA虎@さんのNyARToolkitといい、
スキルの高い開発者の方々が、ARToolKitのさらなるマルチプラットフォーム化を進めています。
これは、より多くの人がARToolKitを使えるようになる、すばらしいことです。
私の開発の目的はあくまで自分の勉強のためですが、
このような方々のように、もっと有用なプログラムを作りたいですね。
あと有用なプログラムといえば、ギズモード・ジャパンで紹介されていた、
ARToolKitを使ったゲームなんてのもあります。
これも非常に面白くて、ARToolKitを上手く生かしてると思いました。
twoViewを使ったプログラム、2回目は特徴色の認識ですが、
正直これだけならtwoViewは関係ありません。
ですがこれができれば、次に面白いことができます。
ということで、軽く説明いたします。
基本は、解析のところでやったことに似ています。
--- < 処理の概要 > ---

処理は以下のようなものになります。
1.認識したい特徴的な色を取得する。
2.カメラに映った画像を、特徴色で2値化する。
3.2値化画像に対して、各閉領域を区別する。
4.閉領域を絞り込んで、適切なものを選択する。
--- < 1.認識したい特徴的な色を取得する > ---
最初に認識したい物体の色を取得します。
この色は、周囲の色とは違う特徴的な色でなくてはいけません。
マウスで画面内の特徴色をクリックし、その色を取得します。
--- < 2.カメラに映った画像を、特徴色で2値化する > ---
次に、与えられた画像を特徴色で2値化します。
画像の全ての点の色を調べ、特徴色に近いものだけ白にして、その他は黒にします。
--- < 3.2値化画像に対して、各閉領域を区別する > ---
白い領域に対してのみラベリングを行い、各閉領域を区別します。
ここでは、異なる色で各閉領域を表しています。
またこのとき、閉領域の面積や外接矩形も計算しておきます。
--- < 4.閉領域を絞り込んで、適切なものを選択する > ---
最後に、特徴色だと判定する閾値を変えたり、
閉領域の面積が小さい・大きいものを除外したりして、目的の閉領域を求めます。
目的の閉領域が、影などにより分かれてしまっていた場合、
外接矩形を見てくっつける、なんてこともできそうです。
さて次はこれを使って、とても面白いプログラムができるはずなのですが、
勉強不足のため、なかなか上手くいきません。
ついつい、学生の頃愛用していた3Dグラフィックス数学の本を買ってしまいました。
もっと数学の勉強をしないとだめです。
正直これだけならtwoViewは関係ありません。
ですがこれができれば、次に面白いことができます。
ということで、軽く説明いたします。
基本は、解析のところでやったことに似ています。
--- < 処理の概要 > ---

処理は以下のようなものになります。
1.認識したい特徴的な色を取得する。
2.カメラに映った画像を、特徴色で2値化する。
3.2値化画像に対して、各閉領域を区別する。
4.閉領域を絞り込んで、適切なものを選択する。
--- < 1.認識したい特徴的な色を取得する > ---
最初に認識したい物体の色を取得します。
この色は、周囲の色とは違う特徴的な色でなくてはいけません。
マウスで画面内の特徴色をクリックし、その色を取得します。
--- < 2.カメラに映った画像を、特徴色で2値化する > ---
次に、与えられた画像を特徴色で2値化します。
画像の全ての点の色を調べ、特徴色に近いものだけ白にして、その他は黒にします。
--- < 3.2値化画像に対して、各閉領域を区別する > ---
白い領域に対してのみラベリングを行い、各閉領域を区別します。
ここでは、異なる色で各閉領域を表しています。
またこのとき、閉領域の面積や外接矩形も計算しておきます。
--- < 4.閉領域を絞り込んで、適切なものを選択する > ---
最後に、特徴色だと判定する閾値を変えたり、
閉領域の面積が小さい・大きいものを除外したりして、目的の閉領域を求めます。
目的の閉領域が、影などにより分かれてしまっていた場合、
外接矩形を見てくっつける、なんてこともできそうです。
さて次はこれを使って、とても面白いプログラムができるはずなのですが、
勉強不足のため、なかなか上手くいきません。
ついつい、学生の頃愛用していた3Dグラフィックス数学の本を買ってしまいました。
もっと数学の勉強をしないとだめです。
ARToolKitのtwoViewを使ったプログラム、その①
カメラ間の関係を保持し、それを使ってマーカーを補間します。
ここでも、安定化と似たような計算を行います。
今回は動画では短すぎるので、画像のみです。


--- < 処理の概要 > ---
まず、どちらのカメラにもマーカーが映っている場合に、以下のような計算をします。
・"マーカー→カメラ1"、"マーカー→カメラ2" が分かっている。
・"マーカー→カメラ1"を反転して"カメラ1→マーカー"にする。
・"カメラ1→マーカー→カメラ2"という行列計算を行い、”カメラ1→カメラ2”を取得する。
次に、片方のカメラでマーカーが見つからなかった場合に、次のような計算で補間を行います。
カメラ1のみでマーカーが見つからなかった場合
・”カメラ1→カメラ2”、"マーカー→カメラ2" が分かっている。
・”カメラ1→カメラ2”を反転して、”カメラ2→カメラ1”にする。
・"マーカー→カメラ2→カメラ1"という行列計算を行い、"マーカー→カメラ1"を取得する。
カメラ2の場合も同様です。
これで、片方のカメラにさえマーカーが映っていれば、
マーカーが見えなくてもその位置を特定することができます。
--- < 処理の特徴 > ---
この方法を使うと安定化にも繋がりますが、そのために以下のような制限が付きます。
・一番最初に、2つのカメラに同じマーカーを映すなどして、変換行列を計算する。
・片方のカメラが動いた場合、変換行列を再計算する。
・両方のカメラでマーカーを発見できないときは、補間ができない。
等々です。
これを解決するためには、いくつか方法が考えられます。
1.カメラを絶対に動かさないようにする。
2.2つのカメラを固定して、変換行列を変えないまま移動できるようにする。
3.カメラ1に、マーカーとカメラ2が必ず映るようにして、カメラ2にもマーカーを付ける。
こんなところでしょうか。
他にも方法がありそうですね。
--- < 最後に > ---
上記のように、twoViewを使うには少し工夫をしないといけないようです。
ですが、そこから得られる機能には、いろんな使い道がありそうです。
カメラ間の関係を保持し、それを使ってマーカーを補間します。
ここでも、安定化と似たような計算を行います。
今回は動画では短すぎるので、画像のみです。
--- < 処理の概要 > ---
まず、どちらのカメラにもマーカーが映っている場合に、以下のような計算をします。
・"マーカー→カメラ1"、"マーカー→カメラ2" が分かっている。
・"マーカー→カメラ1"を反転して"カメラ1→マーカー"にする。
・"カメラ1→マーカー→カメラ2"という行列計算を行い、”カメラ1→カメラ2”を取得する。
次に、片方のカメラでマーカーが見つからなかった場合に、次のような計算で補間を行います。
カメラ1のみでマーカーが見つからなかった場合
・”カメラ1→カメラ2”、"マーカー→カメラ2" が分かっている。
・”カメラ1→カメラ2”を反転して、”カメラ2→カメラ1”にする。
・"マーカー→カメラ2→カメラ1"という行列計算を行い、"マーカー→カメラ1"を取得する。
カメラ2の場合も同様です。
これで、片方のカメラにさえマーカーが映っていれば、
マーカーが見えなくてもその位置を特定することができます。
--- < 処理の特徴 > ---
この方法を使うと安定化にも繋がりますが、そのために以下のような制限が付きます。
・一番最初に、2つのカメラに同じマーカーを映すなどして、変換行列を計算する。
・片方のカメラが動いた場合、変換行列を再計算する。
・両方のカメラでマーカーを発見できないときは、補間ができない。
等々です。
これを解決するためには、いくつか方法が考えられます。
1.カメラを絶対に動かさないようにする。
2.2つのカメラを固定して、変換行列を変えないまま移動できるようにする。
3.カメラ1に、マーカーとカメラ2が必ず映るようにして、カメラ2にもマーカーを付ける。
こんなところでしょうか。
他にも方法がありそうですね。
--- < 最後に > ---
上記のように、twoViewを使うには少し工夫をしないといけないようです。
ですが、そこから得られる機能には、いろんな使い道がありそうです。
作成した処理の可視化プログラムを公開いたします。
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を表示/非表示
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カメラのデバイス名になります。僕の場合は、
この文字列は、もちらんそのカメラ固有のもので、僕のカメラのものを他の人が使っても上手くいきません。
3.ARToolKitの設定ファイルを書き換える
Dataフォルダの"WDM_camera_flipV.xml"というファイルを、適当なエディタで開きます。
その中で、
という行があるはずです。これに"device_name"追加して、以下のようにします。
ここで、先ほど取得したカメラのデバイス名を、device_nameの""内にコピーします。
次に、コピーした文字列で"&"になっている部分を"&"に置き換えます。
この変更したファイルを、別名で保存します。
ここでは、"WDM_camera_flipV_01.xml"とします。
もう1つのデバイス名についても同じ作業をして、名前を変えて保存します。
こっちは、"WDM_camera_flipV_02.xml"としました。
4.プログラムを書き換える
プロジェクトを開いて、twoView.cを変更します。
"WDM_camera_flipV.xml"のところを、新しく作ったファイルに置き換えます。
を
以上で終わりです。
僕の場合は、これでうまくいきました。
これでも失敗する場合は、残念ながら僕には分かりません。
twoViewについては、他にも解説しているサイトがあるので、そちらを参照してください。
Webカメラの種類も関係あるみたいです。
※追記
○08/05/09
一方のカメラのフレームレートだけ、やけに遅い問題が発生していましたが、
"videoWin32DirectShow.cpp"ファイルの
○08/05/10
一方のカメラの画面が上下反転していた問題がありました。
調べてみると、"WDM_camera_flipV_02.xml"ファイルだけ、
このファイルを作るのに、"WDM_camera.xml"を利用したのが間違いだったみたいです。
遅くなってしまったので、コメントを頂いた方はもう解決したかもしれませんが、一応書いておきます。
基本的には、工学ナビ様の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}">
次に、コピーした文字列で"&"になっている部分を"&"に置き換えます。
<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}">
この変更したファイルを、別名で保存します。
ここでは、"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"を利用したのが間違いだったみたいです。
