Plane Sweeping Stereo 〜 実装編1(全体構成)

ということで,下記エントリの続きです.ここから中身に入っていきます.

daily-tech.hatenablog.com

0.全体構成

Plane Sweeping Stereo のコードの全体構成ですが,出来る限り論文の流れに合うようにリファクタしました.大きな関数のフローとしては,下記のようになります.

Step 0. CreatePlanes

ここでは仮想平面の準備をしてます.具体的にはパラメータとして設定した「最大奥行き」「最小奥行き」「仮想平面枚数」の3つのパラメータと「焦点距離」「カメラ間距離の最大値」を元に仮想平面を設置する間隔を計算します.

Step 1. PrepareBuffer

求まったパラメータをベースに,GPUのデバイスメモリを確保します.ちなみに,本家のライブラリではZNCCとかSub Pixelとかもサポートしてるんですが,CUDA の周りがとっても複雑になるのでまずは簡単のために「SAD, Sub Pix無し」の一番簡単なパターンをやってみました.

Step 2. ComputeAccumulationScales

コスト計算のためのパラメータ計算です.SADの場合は,1つのパラメータだけ計算してます.

※Step3 と Step4 はすべての仮想平面に対して計算します.

Step 3. AccumulateCostForPlane

この関数が論文のメインの内容になりますが,検証対象となっている仮想平面上で Reference 画像 と Source 画像のマッチングを実施してます.マッチングの手順としては,
① Source 画像(≠Reference 画像)とReference 画像を仮想平面上で比較するためにホモグラフィー行列を求めます.
② ①で求めたホモグラフィー行列を使って,Reference画像とSource画像の対応するピクセル1つ1つに対してSADを計算します.
③ ②の計算をReference画像の全ての画素に対して実施した時点で,Reference 画像の一つ一つのピクセルに対してコストが計算されています.

Step 3でやっていることを簡単にイメージにしてみました.

f:id:rkoichi2001:20190710002022j:plain
Step3の概略図

Step 4. UpdateBestPlaneBuffer

Step 3で特定の仮想平面に対するコスト計算が完了したので,今までに計算された最良(=最小)コストと今回計算された仮想平面のコストをピクセル1つ1つに対して比較します.今回計算したコストが最良であれば,そのコストと平面の番号を記録しておきます.

Step 4でやっていることを簡単にイメージにしてみました.

f:id:rkoichi2001:20190710002228j:plain
Step 4 の概略図

Step 5. ComputeBestDepth

ループを抜けて,すべての仮想平面に対する検証が完了しているはずなので,記録してある最良コストを確認します.ピクセル1つ1つに対して,最良コストだった平面の番号が記録されているので,そのピクセルの奥行きはその平面の奥行きとなります.

該当部コード

bool PlaneSweepStereo::Run(const int ref_img_idx) {
  LOG(INFO) << "PlaneSweepStereo::Run()";

  // Step 0. Prepare Hypothetical Planes
  CreatePlanes(ref_img_idx, m_buffers->dev_img_map, m_params.num_planes,
               m_params.min_z, m_params.max_z, m_buffers->hyp_planes);

  // Step 1. Buffer Preparaion.
  PrepareBuffer(ref_img_idx);

  // Step 2. Compute Accumulation Scale
  double accum_scale0, accum_scale1;
  CompuateAccumulationScales(accum_scale0, accum_scale1);

  for (unsigned int plane_idx = 0; plane_idx < m_buffers->hyp_planes.size();
       plane_idx++) {
    // Step 3. Compute Cost for THIS plane.
    AccumulateCostForPlane(ref_img_idx, m_buffers->hyp_planes[plane_idx],
                           accum_scale0, accum_scale1);

    // Step 4. Update Best Plane Buffer.
    UpdateBestPlaneBuffer(plane_idx);
  }

  // Step 5. Result Calculation.
  ComputeBestDepth(ref_img_idx);

  return true;
}

ちょっと短いですが,次のエントリで細かいとこを書き留めて最終編にしたいと思います!