14. Multi-Frame Stereo

KITTIのRaw DataのCityを前提

これまで ステレオカメラの左右画像から三次元情報を得る gaze_line-depthモデルというステレオビジョンの新しい考え方について説明してきました. ここでは ステレオビデオカメラからの時間的に連続した 左右画像から得られる情報について考えていきたいと思います. といっても全く任意の動画像ではなくて 市街地をゆっくり走行する車から見える画像のみを対象としたいと思います. 市街地をゆっくり走行する車からの画像として KITTIのRaw DataのCityから2011_09_26_drive_0091を使用させていただきます. KITTIのライセンスは「The Creative Commons Attribution-NonCommercial-ShareAlike 3.0 License」です. ここでは膨大なデータの中から左右340枚のカラー画像を引用しました. この画像の再利用に関してはこのライセンスに従ってください. この340枚のカラーステレオ画像を動画にすると

KITTI 2011_09_26_drive_0091
のようになります.

フレーム間の回転移動情報の検出

市街地をゆっくり走行する車からの画像ですので, 画像の変化の大部分は車が動くことによって発生していると考えることができます. この前提のもと, ある時刻のステレオ画像から得られた三次元形状を回転移動させて 次の時刻のステレオ画像から得られた三次元形状に重ねることを考えます. ステレオビジョン処理で得られた三次元情報がある程度正しくないと 重なったかどうかの判定ができませんので, これはステレオビジョン処理の性能を評価することにもなります. また正しい三次元情報とするには ピクセルを単位とする焦点距離とカメラ間の距離が正しくなければなりません. これが正しくないと回転移動で重ねることができなくなります. 焦点距離とカメラ間の距離はキャリブレーションで得られますから, キャリブレーション処理の性能を評価することにもなります.

それでは例を使ってどんなことなのかを説明していきます. 次の動画はフレーム82とフレーム83を交互に表示するものです.

フレーム82とフレーム83の交互表示
単に車が前方に進んでいることが分かります. 次の動画はフレーム82の三次元形状をフレーム83の三次元形状に重なるように回転移動させている処理中の画像です.
フレーム82を回転移動させる
この三次元形状の回転移動情報から車の移動回転情報が分かります. 今回作成したプログラムからは, 「車は前方へ695mm,左へ6mm,上へ11mm移動した後,水平右に0.16度,垂直下に0.028度,右ねじ方向に0.074度回転した」 という結果が得られました.これがどれほど正しいかは分かりませんが, フレーム82を回転移動させた三次元画像とフレーム83の三次元画像を交互に表示してみると
回転移動させたフレーム82とフレーム83の交互表示
のように,建物は全く変化せずに人物だけが移動しているのが分かります. 中央の自転車が一番大きく移動しています.

次の動画はフレーム0からフレーム30までを車からの見え方として三次元表示したものです.

フレーム0からフレーム30までを車座標で表示
同じ三次元画像をフレーム0の座標で表示すると
フレーム0からフレーム30までを固定座標で表示
となります.建物がほとんど動いていないのが分かります. フレーム間で車がどのように移動回転したかの情報から逆算すると, 移動回転した車から見た三次元形状が 移動回転する前の位置からどう見えるのかを再現することができます. これを複数フレーム間で行うと全てをフレーム0の座標で表示することができます. ただしフレームが進むと回転移動情報の誤差が重なって 見え方のずれも大きくなるはずですが, 建物がほとんど動かないということはフレーム間の回転移動情報がかなり正しく検出されている ということを表しています. 次は同じ三次元画像を前方15mの位置で見たものです.
フレーム0からフレーム30までを前方15m位置で見る
右の壁際を歩く人だけが手を振って移動して行くように見えます. 周りの建物は解像度が変化しますが止まっているようです. 本当は車が前方に進んでいたわけですから 建物も人も後ろへ後ろへ動いていったはずなのにです.

回転移動情報の検出手法

同じカメラによる複数枚の静止物体画像からカメラの配置状態や静止物体の形状を検出する技術は SLAM(Simultaneous Localization and Mapping)と呼ばれ様々な手法が研究されています. 多くの手法は画像処理によりロバストかつ対応付けが容易な特徴点を抽出し, この対応する特徴点の画像位置から三次元の情報を復元しています. ここではそのような画像処理は全く行わずに,三次元の形状そのものを回転移動させています. RRRを回転行列,TTTを移動ベクトルとして, この回転行列と移動ベクトルの最適な値を探すわけですが, 三次元の形状の回転移動は

void trans(int w, int h, int d, int &rw, int &rh, int &rd) {
  double x = -focal*half_base/d;
  double y =  w*half_base/d;
  double z = -h*half_base/d;
  rot_and_mv(RRR, TTT, x, y, z);
  double D = -focal*half_base/x;
  double W =  y*D/half_base;
  double H = -z*D/half_base;
  rw = (int)(W+0.5);
  rh = (int)(H+0.5);
  rd = (int)(D+0.5);
}
のように行います.ここでrot_and_mv(RRR, TTT, x, y, z)はベクトル(x, y, z)を回転移動させた結果を (x, y, z)に上書きする関数です. (w, h)はgaze_lineの位置を表していて,dはその位置のdepthです. focalはピクセルを単位とする焦点距離,half_baseはカメラ間の距離の半分です. 最後の3行は四捨五入を行っています. このコードで分かるように三次元空間の表現もgaze_line-depthモデルとなっています. 最初は点群(Point Cloud)も考えましたが点が近いかどうかの判定が高負荷になるので止めました. 重なったかどうかの評価はgaze_line-depth空間で三次元位置がどれだけ一致するかの割合と 一致したところでの明るさと色を表すRGB値の一致の度合いを使っています. また回転角は0.001度,移動量は1mmを単位として最適な値を検索しています. 検索はカメラの並行化処理でも使った, パラメータを順番に変化させながら最小値を探すという手法で行っています.

前フレームの三次元情報を使った補正

gaze_line-depthモデルのステレオビジョンには二つの問題があります. 一つは一番手前の不連続点で結果がおかしくなること, もう一つはカメラ間の距離が大きいときに奥のものの三次元情報が優先されてしまうということです. まず

フレーム1を様々な角度から見る
を見てください.これはフレーム1の三次元情報を様々な角度から見ているものですが, 線路の間の石畳が窪んでいることが分かると思います. これはより手前の情報がないために起こると考えられます. フレーム0を回転移動してフレーム1に重ねたものは 車が前に進んでいる場合はより手前の情報を含んでいます. この情報を使うと
補正後のフレーム1を様々な角度から見る
のように窪みがなくなります.この手法は車がバックしているときは使えません. これは三次元情報の取得に関し, 車の前進と後退は対称ではないということを表しています. 不思議ですね.