自学内容网 自学内容网

跟着Open3D学C++

前置声明

  • 在很多头文件中有很多这样的代码,参考PointCloud.h
namespace open3d {

namespace camera {
class PinholeCameraIntrinsic;
}

namespace geometry {

class Image;
class RGBDImage;
class TriangleMesh;
class VoxelGrid;
}
}
  • 直接声明其他类型避免在当前头文件中包含这些类型的头文件,减少代码之间的耦合,以及大大的减少编译时间。
  • 缺点就是没包含头文件这些类只能用来声明,不能进行实例化。通常用作函数声明或者使用智能指针进行声明,使用较为受限。
static std::shared_ptr<PointCloud> CreateFromDepthImage(
        const Image &depth,
        const camera::PinholeCameraIntrinsic &intrinsic, // 作为函数声明的参数
        const Eigen::Matrix4d &extrinsic = Eigen::Matrix4d::Identity(),
        double depth_scale = 1000.0,
        double depth_trunc = 1000.0,
        int stride = 1,
        bool project_valid_depth_only = true);

面试考点:基类需要为虚函数

  • 便于继承的类进行资源释放,参考Geometry.h
class Geometry {
public:
    virtual ~Geometry() {}
};

class Geometry3D : public Geometry {
public:
    ~Geometry3D() override {}
}

class PointCloud : public Geometry3D {
    ~PointCloud() override {}
}

限定作用域的枚举

  • 主要是防止命名空间被污染,和cpp尽可能少使用宏的动机一样
enum class GeometryType {
    /// Unspecified geometry type.
    Unspecified = 0,
    /// PointCloud
    PointCloud = 1,
    /// VoxelGrid
    VoxelGrid = 2,
    /// Octree
    Octree = 3,
    /// LineSet
    LineSet = 4,
    /// MeshBase
    MeshBase = 5,
    /// TriangleMesh
    TriangleMesh = 6,
    /// HalfEdgeTriangleMesh
    HalfEdgeTriangleMesh = 7,
    /// Image
    Image = 8,
    /// RGBDImage
    RGBDImage = 9,
    /// TetraMesh
    TetraMesh = 10,
    /// OrientedBoundingBox
    OrientedBoundingBox = 11,
    /// AxisAlignedBoundingBox
    AxisAlignedBoundingBox = 12,
};

函数传参尽量使用const&

  • 参数传递使用引用可减少拷贝构造,尤其是对于复杂类型的参数
  • 任何时候能用const尽量用const,参考effective cpp第三条
PointCloud(const std::vector<Eigen::Vector3d> &points)
    : Geometry3D(Geometry::GeometryType::PointCloud), points_(points) {}

构造函数列表初始化

  • 相比在构造函数中赋值,初始化列表可以在成员变量初始化的时候直接赋值,而不是初始化之后再赋值
PointCloud(const std::vector<Eigen::Vector3d> &points)
    : Geometry3D(Geometry::GeometryType::PointCloud), points_(points) {}

运算符重载

  • 这一点较为普通,很多基础数据类型都会重载常见的运算符
PointCloud &PointCloud::operator+=(const PointCloud &cloud);

最大值最小值的使用

  • 使用cpp风格std::numeric_limits::max()
if (voxel_size * std::numeric_limits<int>::max() <
    (voxel_max_bound - voxel_min_bound).maxCoeff()) {
    utility::LogError("[VoxelDownSample] voxel_size is too small.");
}

随机数的使用

  • 比C语言的rand要更好用
std::random_device rd;
std::mt19937 prng(rd());
std::shuffle(indices.begin(), indices.end(), prng);

tuple的使用

  • 使用起来像python风格
  • 有看到规范说多个变量相同类型相同含义最好还是用vector,不同含义不同类型用struct,用tuple会出现返回值含义不清楚的问题,大家怎么看?
  • 但是比起struct还要单独建立一个类,tuple简直使用起来太方便了。
// 声明
std::tuple<Tensor, Tensor, Tensor> NanoFlannIndex::SearchRadius(
        const Tensor &query_points, const Tensor &radii, bool sort) const;
// 调用
auto [indices, distances, neighbors_row_splits] = NanoFlannIndex::SearchRadius(query_points, radii, sort);

omp加速

  • 大家可以去了解一下,简单的只需要在for循环外加一条omp指令就可以实现多线程加速
#pragma omp parallel for schedule(static) \
        num_threads(utility::EstimateMaxThreads())
    for (int i = 0; i < int(points_.size()); i++) {
        std::vector<int> tmp_indices;
        std::vector<double> dist;
        size_t nb_neighbors = kdtree.SearchRadius(points_[i], search_radius,
                                                  tmp_indices, dist);
        mask[i] = (nb_neighbors > nb_points);
    }
  • 也可以封装起来使用更方便
template <typename func_t>
void ParallelFor(const Device& device, int64_t n, const func_t& func) {
    if (device.GetType() != Device::DeviceType::CPU) {
        utility::LogError("ParallelFor for CPU cannot run on device {}.",
                          device.ToString());
    }
    if (n == 0) {
        return;
    }

#pragma omp parallel for num_threads(utility::EstimateMaxThreads())
    for (int64_t i = 0; i < n; ++i) {
        func(i);
    }
}
  • tbb也使用较多,可以更深入的控制
// Parallel search.
tbb::parallel_for(
        tbb::blocked_range<size_t>(0, num_query_points),
        [&](const tbb::blocked_range<size_t> &r) {
            std::vector<std::pair<index_t, scalar_t>> ret_matches;
            for (size_t i = r.begin(); i != r.end(); ++i) {
                scalar_t radius = radii[i].Item<scalar_t>();
                scalar_t radius_squared = radius * radius;

                size_t num_results = holder->index_->radiusSearch(
                        query_points[i].GetDataPtr<scalar_t>(),
                        radius_squared, ret_matches, params);
                ret_matches.resize(num_results);
                std::vector<index_t> single_indices;
                std::vector<scalar_t> single_distances;
                for (auto it = ret_matches.begin();
                        it < ret_matches.end(); it++) {
                    single_indices.push_back(it->first);
                    single_distances.push_back(it->second);
                }
                batch_indices[i] = single_indices;
                batch_distances[i] = single_distances;
            }
        });

参数使用类进行封装

  • 调用更加清晰明了
class ICPConvergenceCriteria {
public:
    /// \brief Parameterized Constructor.
    ///
    /// \param relative_fitness If relative change (difference) of fitness score
    /// is lower than relative_fitness, the iteration stops. \param
    /// relative_rmse If relative change (difference) of inliner RMSE score is
    /// lower than relative_rmse, the iteration stops. \param max_iteration
    /// Maximum iteration before iteration stops.
    ICPConvergenceCriteria(double relative_fitness = 1e-6,
                           double relative_rmse = 1e-6,
                           int max_iteration = 30)
        : relative_fitness_(relative_fitness),
          relative_rmse_(relative_rmse),
          max_iteration_(max_iteration) {}
    ~ICPConvergenceCriteria() {}

public:
    /// If relative change (difference) of fitness score is lower than
    /// `relative_fitness`, the iteration stops.
    double relative_fitness_;
    /// If relative change (difference) of inliner RMSE score is lower than
    /// `relative_rmse`, the iteration stops.
    double relative_rmse_;
    /// Maximum iteration before iteration stops.
    int max_iteration_;
};

auto result = pipelines::registration::RegistrationColoredICP(
        *source_down, *target_down, 0.07, trans,
        pipelines::registration::
                TransformationEstimationForColoredICP(),
        pipelines::registration::ICPConvergenceCriteria(1e-6, 1e-6,
                                                        iterations[i]));
  • 对比PCL的ICP算法,open3d的不是类,而只是一个函数,使用参数类更加方便。用类和用函数实现大家怎么看?
  • cpp20对于成员变量有更好的方式
// cpp20初始化赋值时可指定成员变量名,更加清晰
struct Temp{
    int a = 0;
    int b = 0;
};

auto temp = Temp{.a = 1, .b = 2};
Temp temp1{.b = 3};

原文地址:https://blog.csdn.net/zhangruijerry/article/details/142859703

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!