跟着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)!