花一点时间深入C++吧(2)

花一点时间深入C++吧(2)

上次说了计算机视觉的工程实践(主要是在opencv里面)中,为什么需要“类”这样东西,以及接口的作用。现在可以了解一般情况下,一个“类”是怎样写出来的。

这次就不举kalman的例子,来换一个常见的例子,PnP问题(perspective n point problem)

一般而言,你可以发现两个文件,一个是PnPProblem.h,而另一个是PnPProblem.cpp

如果你在完成老板布置给你的任务时,需要编写一个类去解决一类问题(这个说法本身也很巧秒),就要向你的工程添加一个h文件以及一个cpp文件

先看一下PnPProblem.h,可以发现,

#ifndef PNPPROBLEM_H_
#define PNPPROBLEM_H_

#include <iostream>

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>

#include "Mesh.h"
#include "ModelRegistration.h"
class PnPProblem
{
          //............
}
//.........
#endif /* PNPPROBLEM_H_ */

这样一个框架结构,开头是#ifndef,然后#define,末尾是#endif(似乎一切都很简单)

再来看class PnPProblem,

class PnPProblem
{

public:
  explicit PnPProblem(const double param[]);  // custom constructor
  virtual ~PnPProblem();

  bool backproject2DPoint(const Mesh *mesh, const cv::Point2f &point2d, cv::Point3f &point3d);
  bool intersect_MollerTrumbore(Ray &R, Triangle &T, double *out);
  std::vector<cv::Point2f> verify_points(Mesh *mesh);
  cv::Point2f backproject3DPoint(const cv::Point3f &point3d);
  bool estimatePose(const std::vector<cv::Point3f> &list_points3d, const std::vector<cv::Point2f> &list_points2d, int flags);
  void estimatePoseRANSAC( const std::vector<cv::Point3f> &list_points3d, const std::vector<cv::Point2f> &list_points2d,
                           int flags, cv::Mat &inliers,
                           int iterationsCount, float reprojectionError, double confidence );

  cv::Mat get_A_matrix() const { return _A_matrix; }
  cv::Mat get_R_matrix() const { return _R_matrix; }
  cv::Mat get_t_matrix() const { return _t_matrix; }
  cv::Mat get_P_matrix() const { return _P_matrix; }

  void set_P_matrix( const cv::Mat &R_matrix, const cv::Mat &t_matrix);

private:
  /** The calibration matrix */
  cv::Mat _A_matrix;
  /** The computed rotation matrix */
  cv::Mat _R_matrix;
  /** The computed translation matrix */
  cv::Mat _t_matrix;
  /** The computed projection matrix */
  cv::Mat _P_matrix;
};

从这段代码中,可以学习到一些知识:

(1)public存放接口函数,private存放需要维护的变量

(2)但凡创建一个对象,就需要一个构造函数。但凡销毁一个对象,就要有构析函数

(3)h文件只是声明这个类,以及该类的接口函数,然而具体实现存放与cpp文件中

(4)大量的注释,解释所要维护变量的意义,以及接口函数的意义,注释方便设计者维护自己的类,也方便使用者使用这些函数

除此之外,还有“类外函数”的概念(C++primer称为相关的非成员函数),比如

// Functions for Möller–Trumbore intersection algorithm
cv::Point3f CROSS(cv::Point3f v1, cv::Point3f v2);
double DOT(cv::Point3f v1, cv::Point3f v2);
cv::Point3f SUB(cv::Point3f v1, cv::Point3f v2);

定义了矢量的点积和叉积。为什么有类外函数呢?其实也好理解,类外,就是不太重要的意思。像点积叉积这些操作都很基础,就不必放到类内。并且,类外的函数不能引用private变量。总之,写一个h文件似乎不太难,格式到位就行,而cpp文件的编写是重头戏

先看cpp文件构造以及构析类PnPProblem的代码,

PnPProblem::PnPProblem(const double params[])
{
  _A_matrix = cv::Mat::zeros(3, 3, CV_64FC1);   // intrinsic camera parameters
  _A_matrix.at<double>(0, 0) = params[0];       //      [ fx   0  cx ]
  _A_matrix.at<double>(1, 1) = params[1];       //      [  0  fy  cy ]
  _A_matrix.at<double>(0, 2) = params[2];       //      [  0   0   1 ]
  _A_matrix.at<double>(1, 2) = params[3];
  _A_matrix.at<double>(2, 2) = 1;
  _R_matrix = cv::Mat::zeros(3, 3, CV_64FC1);   // rotation matrix
  _t_matrix = cv::Mat::zeros(3, 1, CV_64FC1);   // translation matrix
  _P_matrix = cv::Mat::zeros(3, 4, CV_64FC1);   // rotation-translation matrix

}

PnPProblem::~PnPProblem()
{
  // TODO Auto-generated destructor stub
} 

这段代码可以学到几样东西:

(1)注意 :: 这个定义域符号

(2)在h文件中,注意explicit(显式构造)的意义,说明类PnPProblem不能默认构造,必须经过构造函数才能初始化

然后我们随便看一个接口函数,

// Estimate the pose given a list of 2D/3D correspondences and the method to use
bool PnPProblem::estimatePose( const std::vector<cv::Point3f> &list_points3d,
                               const std::vector<cv::Point2f> &list_points2d,
                               int flags)
{
  cv::Mat distCoeffs = cv::Mat::zeros(4, 1, CV_64FC1);
  cv::Mat rvec = cv::Mat::zeros(3, 1, CV_64FC1);
  cv::Mat tvec = cv::Mat::zeros(3, 1, CV_64FC1);

  bool useExtrinsicGuess = false;

  // Pose estimation
  bool correspondence = cv::solvePnP( list_points3d, list_points2d, _A_matrix, distCoeffs, rvec, tvec,
                                      useExtrinsicGuess, flags);

  // Transforms Rotation Vector to Matrix
  Rodrigues(rvec,_R_matrix);
  _t_matrix = tvec;

  // Set projection matrix
  this->set_P_matrix(_R_matrix, _t_matrix);

  return correspondence;
}
最后,简单分析了PnPProblem,可以知道一般的类的编写方法(没有用什么高级技巧,很简单很实用)

还有一个东西比较常用,就是this指针,这个概念很好理解,就不多言了。




猜你喜欢

转载自blog.csdn.net/qq_39732684/article/details/80501309