Bringen Sie Ihnen bei, die visuelle 3D-Rekonstruktion zu beherrschen, eine kleine Bit-Zeile-für-Zeile-Analyse wichtiger Colmap-Codes (Umriss-Aufwärmphase)

Bringen Sie Ihnen ein wenig bei, wie Sie die visuelle 3D-Rekonstruktion meistern – ordnen Sie wichtigen Code Zeile für Zeile zu

Hier soll sich das Colmap-Framework durchsetzen und die Codes wichtiger Links nacheinander in Kombination mit meinen eigenen Ideen erklären Colmap ist die aktuelle State-of-the-Art-Pipeline zur visuellen Rekonstruktion, deren Code ich in zwei Hauptlinks unterteile : Front-End und Back-End Das Front-End dient hauptsächlich der Feature-Extraktion und dem Abgleich, und das Back-End umfasst Triangulation, Register, BA und andere Links.


  • Frist für die Fertigstellung: Ende Dezember; hier ist der Blog als Beweis, wenn Sie der Meinung sind, dass dieser Blog Ihnen geholfen hat, hoffe ich, Ihnen Mut zu machen, und ich werde das Wochenende und meine Freizeit nutzen, um das Schreiben von abzuschließen den gesamten Artikel so schnell wie möglich - Willkommen bei Station B
    : Orange Im RGB-Raum synchronisiert Station B auch die Videoerklärungen der einzelnen Kapitel im Follow-up Gleichzeitig hat Station B viele Videoergebnisse für Alle zum Anschauen!!!
    Gleichzeitig wird das Dokument mit dem Github synchronisiert: address , welcome to star! ! ! ! ! ! ! ! ! ! ! ! ! !

Inhaltsverzeichnis

  1. Merkmalsextraktion

  2. Feature-Matching

  3. Triangulation oder Resektion

  4. sportliche Erholung

  5. Bündelanpassung und Algorithmusverbesserung


Frontend

Extrahieren von Merkmalen

  • sift-GPU-Algorithmus

      待写
    
  • DSP-SIFT-Algorithmus

       待写
    

    Domain-Size Pooling SIFT ist ein Durchschnitt aus mehreren SIFT-Deskriptoren,
    siehe das Paper: Domain-Size Pooling in Local Descriptors and Network Architectures ,
    das sich als überlegen gegenüber anderen Varianten von SIFT-Algorithmen und einigen Deep-Learning-Operatoren erwiesen hat.

Feature-Matching

  • Matching-Methode

       待写
    
  • Geometrievalidierung - Ausreißer entfernen

    1. Verwenden Sie für die Kalibrierungskamera das Verhältnis der Anzahl der internen Punkte von E/F, H/F und H/E, um zu bestimmen, welches Modell verwendet werden soll, um den falschen Punkt zu entfernen

    Code final inlier_matches = ExtractInlierMatches (Übereinstimmungen, Punkte im Modell, Modellmaske)

      //  对于标定相机 利用 E/F H/F H/E 的内点比值来决定使用那种模型来剔除outliers
    
      const double E_F_inlier_ratio =
          static_cast<double>(E_report.support.num_inliers) /
          F_report.support.num_inliers;
      const double H_F_inlier_ratio =
          static_cast<double>(H_report.support.num_inliers) /
          F_report.support.num_inliers;
      const double H_E_inlier_ratio =
          static_cast<double>(H_report.support.num_inliers) /
          E_report.support.num_inliers;
    

    2. Verwenden Sie für Nicht-Standard-Kameras das F-Matrix-Modell

  // 非标相机估计 H/F 的比值即可,E 矩阵无法得到
  const double H_F_inlier_ratio =
      static_cast<double>(H_report.support.num_inliers) /
      F_report.support.num_inliers;

  if (H_F_inlier_ratio > options.max_H_inlier_ratio) {
    
    
    config = ConfigurationType::PLANAR_OR_PANORAMIC;
  } else {
    
    
    config = ConfigurationType::UNCALIBRATED;
  }

  inlier_matches = ExtractInlierMatches(matches, F_report.support.num_inliers,
                                        F_report.inlier_mask);

Gibt es für die Eliminierung von Ausreißern neben der Verwendung des ransac-Schätzmodells zur Eliminierung von Ausreißern
eine Idee, die verbessert werden kann?Wie wir alle wissen, führt ransac jedes Mal eine Stichprobe durch und berechnet das Modell neu, was zeitaufwändig ist.

Heck

Triangulation

Im colmap-Code ist die Triangulation in die folgenden Funktionen unterteilt:

1. Zwei-Frame-Triangulation, d. h. SVD-Zerlegung verwenden, um P (M*P=m) zu lösen, der Code lautet wie folgt:

// proj_matrix1 ,proj_matrix2 分别是世界系到图像系的投影矩阵
Eigen::Vector3d TriangulatePoint(const Eigen::Matrix3x4d& proj_matrix1,
                                 const Eigen::Matrix3x4d& proj_matrix2,
                                 const Eigen::Vector2d& point1,
                                 const Eigen::Vector2d& point2) {
  Eigen::Matrix4d A;

  A.row(0) = point1(0) * proj_matrix1.row(2) - proj_matrix1.row(0);
  A.row(1) = point1(1) * proj_matrix1.row(2) - proj_matrix1.row(1);
  A.row(2) = point2(0) * proj_matrix2.row(2) - proj_matrix2.row(0);
  A.row(3) = point2(1) * proj_matrix2.row(2) - proj_matrix2.row(1);

  Eigen::JacobiSVD<Eigen::Matrix4d> svd(A, Eigen::ComputeFullV);

  return svd.matrixV().col(3).hnormalized();
}

2. Multiframe-Triangulation soll überbestimmte Gleichungen lösen Um die 3D-Szenenstruktur aus mehreren Beobachtungen wiederherzustellen, muss die Triangulation die folgenden zwei Bedingungen erfüllen:

(1) Alle Beobachtungen müssen die Cheiralitätsbeschränkung erfüllen (was ist die Cheiralitätsbeschränkung, d. h. die Einschränkung der positiven Schärfentiefe,
wenn Sie die E-Matrix im Geometriebuch mit mehreren Ansichten zerlegen, erhalten Sie vier Ergebnisse – R, t , aber der Rekonstruktionspunkt ist vor der Kamera nur ein Ergebnis)

(2) Es gibt genügend Triangulationswinkel zwischen Beobachtungspaaren

Das Ergebnis der Lösung muss die beiden oben genannten Bedingungen erfüllen, der Code lautet wie folgt

Bedingung 1: xyz-Cheiralitätseinschränkung

    for (const auto& pose : pose_data) {
      if (!HasPointPositiveDepth(pose.proj_matrix, xyz)) {
        return std::vector<M_t>();
      }
    }

Bedingung 2: Zwischen Bildpaaren sind ausreichende Triangulationswinkel erforderlich (Schwellenwert ist min_tri_angle)

    for (size_t i = 0; i < pose_data.size(); ++i) {
    
    
      for (size_t j = 0; j < i; ++j) {
    
    
        const double tri_angle = CalculateTriangulationAngle(
            pose_data[i].proj_center, pose_data[j].proj_center, xyz);
        if (tri_angle >= min_tri_angle_) {
    
    
          return std::vector<M_t>{
    
    xyz};
        }
      }
    }

3. Robuste LO-RANSAC-Multi-Frame-Triangulation, die hauptsächlich einige falsche Übereinstimmungspunkte eliminiert.
Dies ist auch ein Merkmal der Colmap-Frame-Triangulation. Der Code lautet wie folgt:

bool EstimateTriangulation(
    const EstimateTriangulationOptions& options,
    const std::vector<TriangulationEstimator::PointData>& point_data,
    const std::vector<TriangulationEstimator::PoseData>& pose_data,
    std::vector<char>* inlier_mask, Eigen::Vector3d* xyz) {
    
    
  CHECK_NOTNULL(inlier_mask);
  CHECK_NOTNULL(xyz);
  CHECK_GE(point_data.size(), 2);
  CHECK_EQ(point_data.size(), pose_data.size());
  options.Check();

  // Robustly estimate track using LORANSAC.
  LORANSAC<TriangulationEstimator, TriangulationEstimator,
           InlierSupportMeasurer, CombinationSampler>
      ransac(options.ransac_options);
  ransac.estimator.SetMinTriAngle(options.min_tri_angle);
  ransac.estimator.SetResidualType(options.residual_type);
  ransac.local_estimator.SetMinTriAngle(options.min_tri_angle);
  ransac.local_estimator.SetResidualType(options.residual_type);
  const auto report = ransac.Estimate(point_data, pose_data);
  if (!report.success) {
    
    
    return false;
  }

  *inlier_mask = report.inlier_mask;
  *xyz = report.model;

  return report.success;
}

Registrieren oder Bewegungswiederherstellung

  • Fundamentalmatrix Fundamentalmatrix

    Die Lösung der Fundamentalmatrix geht von folgender Formel aus:

x ′ TF x = 0 \mathbf{x}^{\prime T} F \mathbf{x}=0XT Fx _=0

Die obige Formel kann wie folgt als lineare homogene Gleichung mit 9 Unbekannten geschrieben werden:

ui T f = 0 ui = [ uiui ′ , viui ′ , ui ′ , uivi ′ , vivi ′ , vi ′ , ui , vi , 1 ] T f = [ F 11 , F 12 , F 13 , F 21 , F 22 , F 23 , F 31 , F 32 , F 33 ] T \begin{aligned} &\mathbf{u}_{i}^{T} \mathbf{f}=0\\ &\begin{aligned} \mathbf {u}_{i} &=\left[u_{i} u_{i}^{\prime}, v_{i} u_{i}^{\prime}, u_{i}^{\prime}, u_{i} v_{i}^{\prime}, v_{i} v_{i}^{\prime}, v_{i}^{\prime}, u_{i}, v_{i}, 1\ rechts]^{T} \\ \mathbf{f} &=\left[F_{11}, F_{12}, F_{13}, F_{21}, F_{22}, F_{23}, F_{ 31}, F_{32}, F_{33}\right]^{T} \end{aligned} \end{aligned}uichTF=0uichf=[ uichuich',vichuich',uich',uichvich',vichvich',vich',uich,vich,1 ]T=[ F1 1,F1 2,F1 3,F2 1,F2 2,F2 3,F3 1,F3 2,F3 3]T

Jeder, der MVG gelesen hat, weiß, dass die Basismatrix eine unsichere Skala hat und ihr Rang gleich 2 ist, also ist ihr DOF gleich 7. Daher basiert der Algorithmus zum Schätzen der Basismatrix darauf, den Rang nicht zu ignorieren -2-Einschränkung und Ignorieren der Rang-2-Einschränkung.Die Lösungsmethode kann für 7-Punkte-Methode und 8-Punkte-Methode oder Mehr-Punkte-Methodeunterteilt werden

  1. Der Kern der 7-Punkte-Methode, die die Rang-2-Beschränkung nicht außer Acht lässt, ist die Bestimmung von Lambda und mu, wobei Lambda + mu = 1 ist; aus der obigen Formel ist ersichtlich, dass 9 Unbekannte und 7 Gleichungen offensichtlich nicht lösbar sind , da rank= 2, dann kann der Nullraum der F-Matrix durch die lineare Kombination von f1 und f2 dargestellt werden, also die folgende Formel (siehe Artikel - Determining the Epipolar Geometry and its Uncertainty: A Review, International Journal von Computer Vision ):

F = α F 1 + ( 1 − α ) F 2 \mathbf{F}=\alpha \mathbf{F}_{1}+(1-\alpha) \mathbf{F}_{2}F=ein F1+( 1a ) f2

es ⁡ [ α F 1 + ( 1 − α ) F 2 ] = 0 \operatorname{it}\left[\alpha\mathbf{F}_{1}+(1-\alpha)\mathbf{F}_{ 2} \right]=0d e t[ ein F1+( 1a ) f2]=0

Code zeigen wie folgt:

std::vector<FundamentalMatrixSevenPointEstimator::M_t>
FundamentalMatrixSevenPointEstimator::Estimate(
    const std::vector<X_t>& points1, const std::vector<Y_t>& points2) {
    
    
  CHECK_EQ(points1.size(), 7);
  CHECK_EQ(points2.size(), 7);

 
  Eigen::Matrix<double, 7, 9> A;
  for (size_t i = 0; i < 7; ++i) {
    
    
    const double x0 = points1[i](0);
    const double y0 = points1[i](1);
    const double x1 = points2[i](0);
    const double y1 = points2[i](1);
    A(i, 0) = x1 * x0;
    A(i, 1) = x1 * y0;
    A(i, 2) = x1;
    A(i, 3) = y1 * x0;
    A(i, 4) = y1 * y0;
    A(i, 5) = y1;
    A(i, 6) = x0;
    A(i, 7) = y0;
    A(i, 8) = 1;
  }

  // 9 个未知数,7个方程, 所以我们有两个null space .
  Eigen::JacobiSVD<Eigen::Matrix<double, 7, 9>> svd(A, Eigen::ComputeFullV);
  const Eigen::Matrix<double, 9, 9> f = svd.matrixV();
  Eigen::Matrix<double, 1, 9> f1 = f.col(7);
  Eigen::Matrix<double, 1, 9> f2 = f.col(8);

  f1 -= f2;
 // 很明显,方程个数不够,无法求解,所以我们必须增加约束
 // det(F) = det(lambda * f1 + (1 - lambda) * f2)
 // 其中lambda + mu = 1

  const double t0 = f1(4) * f1(8) - f1(5) * f1(7);
  const double t1 = f1(3) * f1(8) - f1(5) * f1(6);
  const double t2 = f1(3) * f1(7) - f1(4) * f1(6);
  const double t3 = f2(4) * f2(8) - f2(5) * f2(7);
  const double t4 = f2(3) * f2(8) - f2(5) * f2(6);
  const double t5 = f2(3) * f2(7) - f2(4) * f2(6);

  Eigen::Vector4d coeffs;
  coeffs(0) = f1(0) * t0 - f1(1) * t1 + f1(2) * t2;
  coeffs(1) = f2(0) * t0 - f2(1) * t1 + f2(2) * t2 -
              f2(3) * (f1(1) * f1(8) - f1(2) * f1(7)) +
              f2(4) * (f1(0) * f1(8) - f1(2) * f1(6)) -
              f2(5) * (f1(0) * f1(7) - f1(1) * f1(6)) +
              f2(6) * (f1(1) * f1(5) - f1(2) * f1(4)) -
              f2(7) * (f1(0) * f1(5) - f1(2) * f1(3)) +
              f2(8) * (f1(0) * f1(4) - f1(1) * f1(3));
  coeffs(2) = f1(0) * t3 - f1(1) * t4 + f1(2) * t5 -
              f1(3) * (f2(1) * f2(8) - f2(2) * f2(7)) +
              f1(4) * (f2(0) * f2(8) - f2(2) * f2(6)) -
              f1(5) * (f2(0) * f2(7) - f2(1) * f2(6)) +
              f1(6) * (f2(1) * f2(5) - f2(2) * f2(4)) -
              f1(7) * (f2(0) * f2(5) - f2(2) * f2(3)) +
              f1(8) * (f2(0) * f2(4) - f2(1) * f2(3));
  coeffs(3) = f2(0) * t3 - f2(1) * t4 + f2(2) * t5;

  Eigen::VectorXd roots_real;
  Eigen::VectorXd roots_imag;
  if (!FindPolynomialRootsCompanionMatrix(coeffs, &roots_real, &roots_imag)) {
    
    
    return {
    
    };
  }

  std::vector<M_t> models;
  models.reserve(roots_real.size());

  for (Eigen::VectorXd::Index i = 0; i < roots_real.size(); ++i) {
    
    
    const double kMaxRootImag = 1e-10;
    if (std::abs(roots_imag(i)) > kMaxRootImag) {
    
    
      continue;
    }

    const double lambda = roots_real(i);
    const double mu = 1;

    Eigen::MatrixXd F = lambda * f1 + mu * f2;

    F.resize(3, 3);

    const double kEps = 1e-10;
    if (std::abs(F(2, 2)) < kEps) {
    
    
      continue;
    }

    F /= F(2, 2);

    models.push_back(F.transpose());
  }

  return models;
}

  1. Das Ignorieren der Rang-2-Einschränkung wird zu einer 8-Punkte-Methode oder mehr, das heißt, zu diesem Zeitpunkt können wir die Methode der kleinsten Quadrate verwenden, um sie zu lösen

min ⁡ F ∑ ich ( m ~ ich ′ TF m ~ ich ) 2 \min _{\mathbf{F}} \sum_{i}\left(\tilde{\mathbf{m}}_{i}^{\ Primzahl T} \mathbf{F} \tilde{\mathbf{m}}_{i}\right)^{2}MindestFich(M~ich T“.FM~ich)2

min ⁡ f ∥ U nf ∥ 2 \min _{\mathrm{f}}\left\|\mathbf{U}_{n} \mathbf{f}\right\|^{2}Mindestfunf2

Jetzt wird f nur noch von einer unbekannten Skala beeinflusst. Um f = 0 zu vermeiden, müssen wir die Beschränkung erhöhen, selbst wenn der letzte Eigenwert von V in SVD gleich 0 ist (siehe <<Multi-View Geometry in Computer Vision>>). Buchseite 281 für Details), Code-Show wie folgt:

std::vector<FundamentalMatrixEightPointEstimator::M_t>
FundamentalMatrixEightPointEstimator::Estimate(
    const std::vector<X_t>& points1, const std::vector<Y_t>& points2) {
    
    
  CHECK_EQ(points1.size(), points2.size());

  // 中心归一化是为了数值稳定性
  std::vector<X_t> normed_points1;
  std::vector<Y_t> normed_points2;
  Eigen::Matrix3d points1_norm_matrix;
  Eigen::Matrix3d points2_norm_matrix;
  CenterAndNormalizeImagePoints(points1, &normed_points1, &points1_norm_matrix);
  CenterAndNormalizeImagePoints(points2, &normed_points2, &points2_norm_matrix);

  //解决线性方程 x2' * F * x1 = 0.
  Eigen::Matrix<double, Eigen::Dynamic, 9> cmatrix(points1.size(), 9);
  for (size_t i = 0; i < points1.size(); ++i) {
    
    
    cmatrix.block<1, 3>(i, 0) = normed_points1[i].homogeneous();
    cmatrix.block<1, 3>(i, 0) *= normed_points2[i].x();
    cmatrix.block<1, 3>(i, 3) = normed_points1[i].homogeneous();
    cmatrix.block<1, 3>(i, 3) *= normed_points2[i].y();
    cmatrix.block<1, 3>(i, 6) = normed_points1[i].homogeneous();
  }

  // SVD 分解
  Eigen::JacobiSVD<Eigen::Matrix<double, Eigen::Dynamic, 9>> cmatrix_svd(
      cmatrix, Eigen::ComputeFullV);
  const Eigen::VectorXd cmatrix_nullspace = cmatrix_svd.matrixV().col(8);
  const Eigen::Map<const Eigen::Matrix3d> ematrix_t(cmatrix_nullspace.data());

  // 增加约束,即SVD 中的V 的特征向量最后一个等于0 
  Eigen::JacobiSVD<Eigen::Matrix3d> fmatrix_svd(
      ematrix_t.transpose(), Eigen::ComputeFullU | Eigen::ComputeFullV);
  Eigen::Vector3d singular_values = fmatrix_svd.singularValues();
  singular_values(2) = 0.0;
  const Eigen::Matrix3d F = fmatrix_svd.matrixU() *
                            singular_values.asDiagonal() *
                            fmatrix_svd.matrixV().transpose();

  const std::vector<M_t> models = {
    
    points2_norm_matrix.transpose() * F *
                                   points1_norm_matrix};
  return models;
}
  • Essenz-Matrix

    (1) 5-Punkte-Methode zum Lösen

          待写
    

    (2) 8-Punkte-Methode zum Lösen

          待写
    
  • Homographie-Matrix

    Die Lösung der Homographiematrix ist im Vergleich zum Wesentlichen und den Grundlagen relativ einfach. Im Colmap-Code wird der DLT-Algorithmus direkt verwendet, um sie direkt zu lösen. Die theoretische Formel lautet wie folgt: ( uv 1 ) = H ( xy 1 ) \left(\begin{
    array}{l} u \\ v \\ 1\end{array}\right)=H\left(\begin{array}{l}x \\ y \\ 1\end{ Array}\right)uv1=HXj1

wo H:

H = [ h 1 h 2 h 3 h 4 h 5 h 6 h 7 h 8 h 9 ] H = \left[\begin{array}{lll}h_{1} & h_{2} & h_{3} \ \ h_{4} & h_{5} & h_{6} \\ h_{7} & h_{8} & h_{9}\end{array}\right]H=H1H4H7H2H5H8H3H6H9

Linearisierte Entwicklung ergibt:
− h 1 x − h 2 y − h 3 + ( h 7 x + h 8 y + h 9 ) u = 0 -h_{1} x-h_{2} y-h_{3}+ \ left(h_{7} x+h_{8} y+h_{9}\right) u=0h1XH2jH3+( H7X+H8j+H9)u=0
− h 4 x − h 5 y − h 6 + ( h 7 x + h 8 y + h 9 ) v = 0 -h_{4} x-h_{5} y-h_{6}+\left(h_ {7} x+h_{8} y+h_{9}\right) v=0h4XH5jH6+( H7X+H8j+H9)v=0

Definition:
A i = ( − x − y − 1 0 0 0 Wert 0 0 0 − x − y − 1 vxvyv ) A_{i}=\left(\begin{array}{cccccccc}-x & -y &- 1&0&0&0&ux&uy&u\\0&0&0&-x&-y&-1&vx&vy&v\end{array}\right)Aich=(x0j0100 x0 ja0 1du xv xu yv yuv)
h = ( h 1 h 2 h 3 h 4 h 5 h 6 h 7 h 8 h 9 ) h=\left(\begin{array}{cccccc}h_{1} & h_{2} h t_{3} & h_{4} & h_{5} & h_{6} & h_{7} & h_{8} & h_{9}\end{array}\right)_{}H=(H1H2h t3H4H5H6H7H8H9)

Unter Verwendung der Methode der kleinsten Quadrate zur Lösung überbestimmter Gleichungen kann der Code wie folgt erhalten werden:

std::vector<HomographyMatrixEstimator::M_t> HomographyMatrixEstimator::Estimate(
    const std::vector<X_t>& points1, const std::vector<Y_t>& points2) {
    
    
  CHECK_EQ(points1.size(), points2.size());

  const size_t N = points1.size();

  // 中心归一化,数值稳定性
  std::vector<X_t> normed_points1;
  std::vector<Y_t> normed_points2;
  Eigen::Matrix3d points1_norm_matrix;
  Eigen::Matrix3d points2_norm_matrix;
  CenterAndNormalizeImagePoints(points1, &normed_points1, &points1_norm_matrix);
  CenterAndNormalizeImagePoints(points2, &normed_points2, &points2_norm_matrix);

  //构建方程
  Eigen::Matrix<double, Eigen::Dynamic, 9> A = Eigen::MatrixXd::Zero(2 * N, 9);

  for (size_t i = 0, j = N; i < points1.size(); ++i, ++j) {
    
    
    const double s_0 = normed_points1[i](0);
    const double s_1 = normed_points1[i](1);
    const double d_0 = normed_points2[i](0);
    const double d_1 = normed_points2[i](1);

    A(i, 0) = -s_0;
    A(i, 1) = -s_1;
    A(i, 2) = -1;
    A(i, 6) = s_0 * d_0;
    A(i, 7) = s_1 * d_0;
    A(i, 8) = d_0;

    A(j, 3) = -s_0;
    A(j, 4) = -s_1;
    A(j, 5) = -1;
    A(j, 6) = s_0 * d_1;
    A(j, 7) = s_1 * d_1;
    A(j, 8) = d_1;
  }

  // SVD 求解
  Eigen::JacobiSVD<Eigen::Matrix<double, Eigen::Dynamic, 9>> svd(
      A, Eigen::ComputeFullV);

  const Eigen::VectorXd nullspace = svd.matrixV().col(8);
  Eigen::Map<const Eigen::Matrix3d> H_t(nullspace.data()); // 单应矩阵

  const std::vector<M_t> models = {
    
    points2_norm_matrix.inverse() *
                                   H_t.transpose() * points1_norm_matrix};
  return models;
}
  • Relative Pose schätzen (2D-2D)

     待写
    
  • Schätzen Sie die absolute Pose (2D-3D)

     待写
    

Bündelanpassung

  • klassischer Reprojektionsfehler

         待写
    
  • Pose ist ein konstanter Reprojektionsfehler

         待写
    
  • Reprojektionsfehler der partiellen Parameteroptimierung (wie R oder T) ~ Selbstverbesserung

        待写
    
  • GPS-bedingter Reprojektionsfehler (SfM Fused GPS) ~ Selbstverbesserung

         待写
    
  • GCP Constrained Reprojection Error (Marker SfM) ~ Selbstverbesserung

       待写
    
  • Reprojektionsfehler von IMU-Winkelbeschränkungen ~ Selbstverbesserung

        待写
    

おすすめ

転載: blog.csdn.net/qq_15642411/article/details/115528375