[データ関連付け] パッチに基づく対応する特徴の関連付け、現在のフレーム -> 参照フレームの関連付け、フレーム間トラッキング

ここに画像の説明を挿入

1. WarpPixelWise (現在のフレームの特徴点の位置を見つける)

1.1 機能

これは、現在のフレームのカメラポーズに従って、参照フレームの特徴点の周囲のピクセルを現在のフレームに投影し、そのピクセル値を指定された配列に格納します。

1.2 関数の入出力

入力: 関数の入力には、現在のフレーム、参照フレーム、参照フレーム内の特徴点、参照フレームと現在のフレームのピラミッド レベル、ハーフパッチ サイズが含まれます出力: 配列へのポインター出力
ピクセル値の。

1.3 アルゴリズムのステップ

  1. 参照フレーム内の特徴点から参照フレーム カメラまでの距離と、現在のフレーム カメラまでの距離を計算します。
  2. 参照フレームのカメラ座標系の特徴点の座標を 3 次元空間に逆投影し、カメラまでの距離に応じてスケーリングして 3 次元座標を取得します。
  3. 3 次元座標を現在のフレームのカメラ座標系に変換し、それを現在のフレーム画像に投影してピクセル座標を取得します。
  4. ピクセル座標を検索レベルにスケールし、パッチ サイズの半分以内のピクセルごとに次の操作を実行します。
    • ピクセル座標を参照フレーム カメラ座標系の 3 次元座標に変換します。
    • 3 次元座標を参照フレーム画像に投影してピクセル座標を取得します。
    • ピクセル座標を参照フレームのピラミッド レベルにスケールします。
    • 双一次補間に従って参照フレーム イメージ上のこのピクセルの値を計算し、出力配列に格納します。

数式は次のように説明されます。

pcur = T curworld T refworld − 1 pref \mathbf{p}_{cur} = \mathbf{T}_{cur}^{world} \mathbf{T}_{ref}^{world^{-1}} \mathbf{p}_{ref}pさあ_ _=T現在_ _wまたはld _T参照_wまたはld _ 1pリフレ_

pref = dref ∥ prefcam ∥ prefcam \mathbf{p}_{ref} = \frac{d_{ref}}{\|\mathbf{p}_{ref}^{cam}\|}\mathbf{p}_ {参照}^{カム}pリフレ_=∥p _参照_さあ_dリフレ_p参照_さあ_

pcursearch = pcur 2 levelcur \mathbf{p}_{cur}^{search} = \frac{\mathbf{p}_{cur}}{2^{level_{cur}}}p現在_ _シーアラジ_ _ _=2レベル_ _ _ _さあ_ _pさあ_ _

pelesearch = pelepatch + pcursearch \mathbf{p}_{ele}^{search} = \mathbf{p}_{ele}^{patch} + \mathbf{p}_{cur}^{search}pそしてシーアラジ_ _ _=pそしてパッチ_ _ _ _+p現在_ _シーアラジ_ _ _

pelecam = pelesearch 2 levelcur \mathbf{p}_{ele}^{cam} = \frac{\mathbf{p}_{ele}^{search}}{2^{level_{cur}}}pそしてさあ_=2レベル_ _ _ _さあ_ _pそしてシーアラジ_ _ _

peleworld = T refworld T curworld − 1 pelecam \mathbf{p}_{ele}^{world} = \mathbf{T}_{ref}^{world} \mathbf{T}_{cur}^{world^{ -1}} \mathbf{p}_{ele}^{cam}pそしてwまたはld _=T参照_wまたはld _T現在_ _wまたはld _ 1pそしてさあ_

peleref = 1 2 levelref K refpelecam \mathbf{p}_{ele}^{ref} = \frac{1}{2^{level_{ref}}}\mathbf{K}_{ref}\mathbf{p} _{エレ}^{カム}pそしてリフレ_=2レベル_ _ _ _リフレ_1Kリフレ_pそしてさあ_

I ( firstref ) = w 00 I ( ⌊ first , x ⌋ , ⌊ first , y ⌋ ) + w 01 I ( ⌊ first , x ⌋ , ⌊ first , y ⌋ + 1 ) + w 10 I ( ⌊ first , x ⌋ + 1 , ⌊ 前 , y ⌋ ) + w 11 I ( ⌊ 前 , x ⌋ + 1 , ⌊ 前 , y ⌋ + 1 ) I(\mathbf{p}_{ele}^{ref}) = \\ w_ {00}I(\lfloor \mathbf{p}_{ele,x}\rfloor,\lfloor \mathbf{p}_{ele,y}\rfloor) + w_{01}I(\lfloor \mathbf{p }_{ele,x}\rfloor,\lfloor \mathbf{p}_{ele,y}\rfloor+1) + \\w_{10}I(\lfloor \mathbf{p}_{ele,x} \rfloor+1,\lfloor \mathbf{p}_{ele,y}\rfloor) + w_{11}I(\lfloor \mathbf{p}_{ele,x}\rfloor+1,\lfloor \mathbf {p}_{ele,y}\rfloor+1)( pそしてリフレ_)=w00( ⌊pe l e x⌊p __ _⌋)+w01( ⌊pe l e x⌊p __ _+1 )+w10( ⌊pe l e x+1 ⌊p __ _⌋)+w11( ⌊pe l e x+1 ⌊p __ _+1 )

ここで、T \mathbf{T}T はカメラのポーズ、p \mathbf{p}p はピクセル座標を表し、ddd は距離を表します。∥ ⋅ ∥ \|\cdot\|∥ はベクトルK \mathbf{K}の係数を表しますK はカメラの内部参照行列I ( ⋅ ) I(\cdot)I ( )は、画像上の特定のピクセルの値、w 00 、w 01 、w 10 、w 11 w_{00},w_{01},w_{10},w_{11} をw00w01w10w11双一次補間の重みを表します。

bool WarpPixelWise(const Frame& cur_frame, const Frame& ref_frame, const FeatureWrapper& ref_ftr,
    const int level_ref, const int level_cur, const int half_patch_size, uint8_t* patch) {
    
    
  double depth_ref = (ref_frame.pos() - ref_ftr.landmark->pos()).norm();
  double depth_cur = (cur_frame.pos() - ref_ftr.landmark->pos()).norm();

  // back project to 3D points in reference frame
  Eigen::Vector3d xyz_ref;
  ref_frame.cam()->backProject3(ref_ftr.px, &xyz_ref);
  xyz_ref = xyz_ref.normalized() * depth_ref;

  // project to current frame and convert to search level
  Eigen::Vector3d xyz_cur = cur_frame.T_cam_world() * (ref_frame.T_cam_world().inverse()) * xyz_ref;
  Eigen::Vector2d px_cur;
  cur_frame.cam()->project3(xyz_cur, &px_cur);
  Eigen::Vector2d px_cur_search = px_cur / (1 << level_cur);

  // for each pixel in the patch(on search level):
  // - convert to image level
  // - back project to 3D points
  // - project to ref frame and find pixel value in ref level
  uint8_t* patch_ptr = patch;
  const cv::Mat& img_ref = ref_frame.img_pyr_[level_ref];
  const int stride = img_ref.step.p[0];

  for (int y = -half_patch_size; y < half_patch_size; ++y) {
    
    
    for (int x = -half_patch_size; x < half_patch_size; ++x, ++patch_ptr) {
    
    
      const Eigen::Vector2d ele_patch(x, y);
      Eigen::Vector2d ele_search = ele_patch + px_cur_search;
      Eigen::Vector3d ele_xyz_cur;
      cur_frame.cam()->backProject3(ele_search * (1 << level_cur), &ele_xyz_cur);
      ele_xyz_cur = ele_xyz_cur.normalized() * depth_cur;
      Eigen::Vector3d ele_xyz_ref =
          ref_frame.T_cam_world() * (cur_frame.T_cam_world().inverse()) * ele_xyz_cur;
      Eigen::Vector2d ele_ref;
      ref_frame.cam()->project3(ele_xyz_ref, &ele_ref);
      ele_ref = ele_ref / (1 << level_ref);

      const int xi = std::floor(ele_ref[0]);
      const int yi = std::floor(ele_ref[1]);
      if (xi < 0 || yi < 0 || xi + 1 >= img_ref.cols || yi + 1 >= img_ref.rows) {
    
    
        VLOG(200) << "ref image: col-" << img_ref.cols << ", row-" << img_ref.rows;
        VLOG(200) << "xi: " << xi << ", "
                  << "yi: " << yi;
        return false;
      } else {
    
    
        const float subpix_x = ele_ref[0] - xi;
        const float subpix_y = ele_ref[1] - yi;
        const float w00 = (1.0f - subpix_x) * (1.0f - subpix_y);
        const float w01 = (1.0f - subpix_x) * subpix_y;
        const float w10 = subpix_x * (1.0f - subpix_y);
        const float w11 = 1.0f - w00 - w01 - w10;
        const uint8_t* const ptr = img_ref.data + yi * stride + xi;
        *patch_ptr = static_cast<uint8_t>(
            w00 * ptr[0] + w01 * ptr[stride] + w10 * ptr[1] + w11 * ptr[stride + 1]);
      }
    }
  }

  return true;
}

2. GetWarpMatrixAffine (現在のフレーム -> 参照フレームのアフィン変換行列を計算)

2.1 機能

この関数の役割は、現在のフレームの参照フレームの特徴点の投影が現在のフレームの対応するパッチと一致するように、現在のフレームから参照フレームへのアフィン変換行列を計算することです。

2.2 関数の入出力

入力: 関数の入力には、参照フレームと現在のフレームのカメラ パラメーター、参照フレーム内の特徴点、特徴点の方向ベクトル、特徴点から参照フレームのカメラまでの距離、姿勢が含まれます。現在のフレームと参照フレームの間の変換、および参照フレームのピラミッド レベル。
Output: 出力アフィン変換行列へのポインタ。

2.3 アルゴリズムのステップ

関数の実装手順は次のとおりです。

  1. 特徴点の方向ベクトルと距離に従って 3 次元座標を計算します。
  2. 参照フレーム内で水平および垂直方向に沿ってパッチ サイズの半分を移動し、移動したピクセル座標を 3 次元空間に逆投影して、2 方向の 3 次元ベクトルを取得します。
  3. 3 次元ベクトルを現在のフレームのカメラ座標系に変換し、それを現在のフレームに投影して 3 つのピクセル座標を取得します。
  4. 3 つのピクセル座標からアフィン変換行列を計算します。

数式は次のように説明されます。

xref = frefdrefxdu 、 ref = J du 、 refxrefxdv 、 ref = J dv 、 refxrefxcur = T cur _ refxrefxdu 、 cur = T cur _ refxdu 、 refxdv 、 cur = T cur _ refxdv 、 ref A cur _ ref = [ xdu , cur − xcurk 半分のパッチ サイズ izexdv 、 cur − xcurk 半分のパッチ サイズ ] \begin{aligned} \mathbf{x}_{ref} &= \mathbf{f}_{ref} d_{ref} \\ \ mathbf{x}_{du,ref} &= \mathbf{J}_{du,ref} \mathbf{x}_{ref} \\ \mathbf{x}_{dv,ref} &= \mathbf{ J}_{dv,ref} \mathbf{x}_{ref} \\ \mathbf{x}_{cur} &= T_{cur\_ref} \mathbf{x}_{ref} \\ \mathbf{ x}_{du,cur} &= T_{cur\_ref} \mathbf{x}_{du,ref} \\ \mathbf{x}_{dv,cur} &= T_{cur\_ref} \mathbf {x}_{dv,ref} \\ \mathbf{A}_{cur\_ref} &= \begin{bmatrix}\frac{\mathbf{x}_{du,cur}-\mathbf{x}_{cur}}{kHalfPatchSize} & \frac{\mathbf{x}_{dv,cur}-\mathbf{x}_{cur}}{kHalfPatchSize}\end{bmatrix} \end{整列}バツリフレ_バツあなた参照しくださいバツdv ref _ _バツさあ_ _バツ_ _ _バツdv curr _ _ _キュル_リフレ_ _ _=fリフレ_dリフレ_=Jあなた参照しくださいバツリフレ_=Jdv ref _ _バツリフレ_=Tキュル_リフレ_ _ _バツリフレ_=Tキュル_リフレ_ _ _バツあなた参照しください=Tキュル_リフレ_ _ _バツdv ref _ _=[kパッチサイズ半分_ _ _ _ _ _ _ _バツ_ _ _×さあ_ _kパッチサイズ半分_ _ _ _ _ _ _ _バツdv curr _ _ _×さあ_ _]

ここでfref \mathbf{f}_{ref}fリフレ_特徴点の方向ベクトルを表します、dref d_{ref}dリフレ_特徴点から参照フレーム カメラまでの距離を示しますxref \mathbf{x}_{ref}バツリフレ_参照フレームのカメラ座標系xdu , ref \mathbf{x}_{du,ref}における特徴点の 3 次元座標を示します。バツあなた参照しくださいxdv, ref \mathbf{x}_{dv,ref}バツdv ref _ _水平方向と垂直方向にパッチ サイズの半分を移動した後の参照フレーム内の 3 次元座標を示します。J du , ref \mathbf{J}_{du,ref}Jあなた参照しくださいJ dv , ref \mathbf{J}_{dv,ref}Jdv ref _ _パッチサイズの半分を水平方向と垂直方向に移動した後の参照フレームカメラ座標系の対応するピクセル座標に対応する 3 次元ベクトルをそれぞれ表します、 T cur_ref T_{cur\_ref }Tキュル_リフレ_ _ _現在のフレームと参照フレーム間のポーズ変換を表します、xcur \mathbf{x}_{cur}バツさあ_ _xdu 、 cur \mathbf{x}_{du,cur}バツ_ _ _xdv, cur\mathbf{x}_{dv,cur}バツdv curr _ _ _それぞれxref \mathbf{x}_{ref}を表しますバツリフレ_xdu 、 ref \mathbf{x}_{du,ref}バツあなた参照しくださいxdv, ref \mathbf{x}_{dv,ref}バツdv ref _ _現在のフレームのカメラ座標系に変換して得られる 3 次元座標xdu , cur − xcurk Half Patch Size \frac{\mathbf{x}_{du,cur}-\mathbf{x}_ {cur}} {kHalfPatchSize}kパッチサイズ半分_ _ _ _ _ _ _ _バツ_ _ _×さあ_ _xdv , cur − xcurk ハーフパッチサイズ \frac{\mathbf{x}_{dv,cur}-\mathbf{x}_{cur}}{kHalfPatchSize}kパッチサイズ半分_ _ _ _ _ _ _ _バツdv curr _ _ _×さあ_ _はそれぞれ、水平方向と垂直方向にパッチ サイズの半分を移動した後の、現在のフレームのカメラ座標系の対応するピクセル座標に対応する 3 次元ベクトルを表します。 A cur _ ref \mathbf{A}_{cur\_ref}キュル_リフレ_ _ _現在のフレームから参照フレームへのアフィン変換行列を表します。

void GetWarpMatrixAffine(const CameraPtr& cam_ref, const CameraPtr& cam_cur,
    const Eigen::Ref<Keypoint>& px_ref, const Eigen::Ref<BearingVector>& f_ref,
    const double depth_ref, const Transformation& T_cur_ref, const int level_ref,
    AffineTransform* A_cur_ref) {
    
    
  CHECK_NOTNULL(A_cur_ref);

  // Compute affine warp matrix A_ref_cur
  const int kHalfPatchSize = 5;
  const Position xyz_ref = f_ref * depth_ref;
  Position xyz_du_ref, xyz_dv_ref;
  // NOTE: project3 has no guarantee that the returned vector is unit length
  // - for pinhole: z component is 1 (unit plane)
  // - for omnicam: norm is 1 (unit sphere)
  cam_ref->backProject3(
      px_ref + Eigen::Vector2d(kHalfPatchSize, 0) * (1 << level_ref), &xyz_du_ref);
  cam_ref->backProject3(
      px_ref + Eigen::Vector2d(0, kHalfPatchSize) * (1 << level_ref), &xyz_dv_ref);
  if (cam_ref->getType() == Camera::Type::kPinhole) {
    
    
    xyz_du_ref *= xyz_ref[2];
    xyz_dv_ref *= xyz_ref[2];
  } else {
    
    
    xyz_du_ref.normalize();
    xyz_dv_ref.normalize();
    xyz_du_ref *= depth_ref;
    xyz_dv_ref *= depth_ref;
  }

  Keypoint px_cur, px_du_cur, px_dv_cur;
  cam_cur->project3(T_cur_ref * xyz_ref, &px_cur);
  cam_cur->project3(T_cur_ref * xyz_du_ref, &px_du_cur);
  cam_cur->project3(T_cur_ref * xyz_dv_ref, &px_dv_cur);
  A_cur_ref->col(0) = (px_du_cur - px_cur) / kHalfPatchSize;
  A_cur_ref->col(1) = (px_dv_cur - px_cur) / kHalfPatchSize;
}



3. GetWarpMatrixAffine (現在のフレーム -> 参照フレームのアフィン変換行列を計算)

3.1 機能

この関数の機能は、最高のパフォーマンスを得るためにどのピラミッド レベルで一致点を検索するかを計算することです。

3.2 関数の入出力

入力: その入力には、アフィン変換行列と最大ピラミッド レベルが含まれます。
出力: 出力はピラミッド レベルです。

3.3 アルゴリズムのステップ

  1. アフィン変換行列の行列式 D を計算します。
  2. D が 3.0 より大きく、検索レベルが最大ピラミッド レベルより小さい場合は、D が 3.0 以下になるか、検索レベルが最大ピラミッド レベルに達するまで、検索レベルに 1 を加えて D に 0.25 を乗算します。

数式は次のように説明されます。

search _ level = min ⁡ ( max ⁡ _ level , max ⁡ { n ∣ D ( A cur _ ref ) > 3. 0 n } ) search\_level = \min(\max\_level, \max\{n|D) (A_{cur\_ref}) > 3.0^n\})シーアーク_レベル_ _ _ _ _ _ _=最小(最大_レベル_ _ _ _最大{ n D ( Aキュル_リフレ_ _ _)>3.0 _n })

ただし、D ( A cur _ ref ) D(A_{cur\_ref})D ( Aキュル_リフレ_ _ _)アフィン変換行列A cur _ ref A_{cur\_ref}キュル_リフレ_ _ _の決定要因。

この関数の原理は、アフィン変換行列の不確実性に応じて、検索するピラミッド レベルを選択することです。アフィン変換行列がより不確実な場合、より高いマッチング パフォーマンスを得るためにより高いピラミッド レベルで検索する必要があります。アフィン変換行列がより明確である場合、計算効率を向上させるために下位のピラミッド レベルで検索を実行できます。


int GetBestSearchLevel(const AffineTransform& A_cur_ref, const int max_level) {
    
    
  // Compute patch level in other image
  int search_level = 0;
  double D = A_cur_ref.determinant();
  while (D > 3.0 && search_level < max_level) {
    
    
    search_level += 1;
    D *= 0.25;
  }
  return search_level;
}

おすすめ

転載: blog.csdn.net/Darlingqiang/article/details/131332430