ターゲット座標を取得

顔認識で取得した情報から3Dの座標を算出します。

ScfpcGetPos int p_CameraID, double p_cpl_PosX_From_Center, double p_cpl_PosY_From_Center, double p_cpl_Size, double p_rsp_Size, var pv_csp_PosX, var pv_csp_PosY, var pv_csp_PosZ

[IN] p_CameraID = 0~(0) : カメラID
[IN] p_cpl_PosX_From_Center = (0.0) : カメラ画像中心を0とした平面座標X [px]
[IN] p_cpl_PosY_From_Center = (0.0) : カメラ画像中心を0とした平面座標Y [px]
[IN] p_cpl_Size = (0.0) : カメラ画像上のサイズ [px]
[IN] p_rsp_Size = (0.0) : 実際のサイズ [unit]
[OUT] pv_csp_PosX : 検出座標X [unit]
[OUT] pv_csp_PosY : 検出座標Y [unit]
[OUT] pv_csp_PosZ : 検出座標Z [unit]

クリックで拡大 右の図を解説。
左側がHSPCVの顔認識の図。右側がこのモジュールで使用する座標系の図。
黒枠:取得した映像の枠。
青い四角:顔認識で取得された顔の枠。

カメラID
初期設定で説明したので省略。

カメラ画像中心を0とした平面座標
右の図を見てわかるとおり、画像の中心位置を(0, 0)とした座標系。
赤×の座標を指定します。

うん。こりゃ、わかりにくい。
でもこうすると3D座標座標にした時に座標軸の中心位置が2Dと3Dで一致するのでわかりやすくなるんです。

カメラ画像上のサイズ
cvgetface命令の第3,4引数で取得される値で、顔認識で取得したサイズです。
右の図で言えば青い枠の幅と高さがそれです。

実際のサイズ
画面上で青枠で表示されている枠が、実際…カメラの外の実物ではいくらの寸法かを指定します。
顔認識で取得した画像を見ながら、定規を顔に当てて調べてもいいですし、 任意の数値を指定しても構いません。

但し、ここで指定した単位は、検出座標に反映されるので重要です。
「cm」で指定すれば検出座標で取得されるすうちも「cm」になります。
「インチ」で指定すれば検出座標で取得されるすうちも「インチ」になります。
「3D空間内の単位でいくつ」と指定すれば検出座標はそのまま「3D空間内での寸法」になります。 これが一番あつかい易いかもしれませんね。

検出座標
クリックで拡大 取得した情報を元に算出した3D空間座標を出力します。
ここで算出される数値の単位は「実際のサイズ」で使用した単位を使用します。
取得した値をEasy3Dなりhgimg3に使えばいいわけです。3Dに慣れている人ならすぐ気づくと思いますが、 ここで重要なのは原点の位置座標系です。ちょっとわかりにくいのでこちらも図を起こしました。右図参照。

カメラのレンズがある位置を原点とした座標系となっています。X,Yの方向をディスプレイの表示に合わせた右手系です。

もちろんそのままではEasy3Dやhgimg3で使用できないので適当に-1.0掛けるなどして使ってください。

プログラミングのポイント

ちなみに…上述しましたが、これ、カメラレンズ位置が原点なんです。
そう、ディスプレイの中心が原点ではないんです。
通常ディスプレイとカメラは、相対位置も相対角度も不定です。 webカメラをディスプレイの上につけるか、下につけるかはユーザー次第。もしかしたら横においてるかもしれません。これはユーザーによって異なります。
この前提を忘れてプログラムしてしまうと、ディスプレイに近づいたら思ったとおりの座標が取得できない!なんてことになります。

検出精度の誤差にはこういった要因があることを忘れないようにしてください。
奥の手として、事前にユーザーにカメラの設置位置やディスプレイのインチ数を聞いておくのも一つの手だと思います。

あるいはiPodなどのように、カメラとディスプレイが固定であればこの問題は解決します。 しかもディスプレイのインチ数もほぼ固定なのでやりやすいでしょうね。