一、算法原理
最小生成树对法向量定向的结果在具有许多尖锐特征和遮挡的机载点云数据中结果并不理想。scanline_orient_normals()
是专门用于具有扫描线特性的点云法向量重定向的替代方法。它充分利用了某些激光雷达扫描器的LAS特性,是处理2.5D城市场景中车载或机载点云法向量方向重定向的最佳选择。
1、主要函数
头文件
#include <CGAL/scanline_orient_normals.h>
scanline_orient_normals()
void CGAL::scanline_orient_normals ( PointRange & points,
const NamedParameters & np = parameters::default_values()
)
通过检查扫描视线方向与当前法线方向的一致性,对点云的法向量进行定向。这个函数要求输入的点是沿着xy平面上对齐的扫描线排序。这种数据通常是通过机载或车载激光雷达设备获得。当提供scanline_id_map
和scan_angle
时,该方法会给出最优结果。只要点集在2.5D扫描线中有序排列,在缺少其中一个或两个属性的情况下,仍然可以生成正确的结果。
- 首先,通过对点进行迭代,获取位于同一扫描线上的点:
- 如果提供了命名参数
scanline_id_map
,则每次id更改时范围都会被切断。 - 如果没有提供扫描线ID映射,回退方法只是简单地在投影xy平面上每次有3个连续点形成锐角时削减范围。这种回退方法给出了次优结果。
- 然后,估计每个点与扫描器位置之间的视线(采集时的估计矢量):
- 如果提供了
scan_angle
,视线可以直接计算为估计扫描线和扫描角度的组合。 - 如果没有提供扫
scan_angle
,扫描仪的位置估计为在xy平面上投影扫描线各点的重心之上。这种回退方法给出了次优结果。
一旦对每个点的视线进行了估计,就可以通过视点来调整法向量的方向,如果视线和法线向量乘积为正则法向量方向为正的,否则法向量是反的。
二、代码实现
#include <CGAL/Simple_cartesian.h>
#include <CGAL/IO/read_las_points.h>
#include <CGAL/IO/write_ply_points.h>
#include <CGAL/jet_estimate_normals.h>
#include <CGAL/scanline_orient_normals.h>
using Kernel = CGAL::Simple_cartesian<double>;
using Point_with_info = std::tuple<Kernel::Point_3, Kernel::Vector_3, float, unsigned char>;
using Point_map = CGAL::Nth_of_tuple_property_map<0, Point_with_info>;
using Normal_map = CGAL::Nth_of_tuple_property_map<1, Point_with_info>;
using Scan_angle_map = CGAL::Nth_of_tuple_property_map<2, Point_with_info>;
using Scanline_id_map = CGAL::Nth_of_tuple_property_map<3, Point_with_info>;
void dump(const char* filename, const std::vector<Point_with_info>& points)
{
std::ofstream ofile(filename, std::ios::binary);
CGAL::IO::set_binary_mode(ofile);
CGAL::IO::write_PLY(ofile, points, CGAL::parameters::point_map(Point_map()).
normal_map(Normal_map()));
}
int main(int argc, char** argv)
{
std::string fname( "cgal//urban.las");
std::vector<Point_with_info> points;
// ----------------------------------读取las点云--------------------------------------
std::cerr << "Reading input file " << fname << std::endl;
std::ifstream ifile(fname, std::ios::binary);
if (!ifile ||
!CGAL::IO::read_LAS_with_properties(ifile, std::back_inserter(points),
CGAL::IO::make_las_point_reader(Point_map()),
std::make_pair(Scan_angle_map(),CGAL::IO::LAS_property::Scan_angle()),
std::make_pair(Scanline_id_map(),CGAL::IO::LAS_property::Scan_direction_flag())))
{
std::cerr << "Can't read " << fname << std::endl;
return -1;
}
// --------------------------------计算法向量----------------------------------------
std::cerr << "Estimating normals" << std::endl;
CGAL::jet_estimate_normals<CGAL::Parallel_if_available_tag>(points, 12,
CGAL::parameters::point_map(Point_map()).normal_map(Normal_map()));
// ---------------------使用扫描角度和扫描方向重定向法线-----------------------------
std::cerr << "Orienting normals using scan angle and direction flag" << std::endl;
CGAL::scanline_orient_normals(points,CGAL::parameters::point_map(Point_map()).
normal_map(Normal_map()).scan_angle_map(Scan_angle_map()).scanline_id_map(Scanline_id_map()));
dump("out_angle_and_flag.ply", points);
// ---------------------使用扫描方向对点云进行法线定向-------------------------------
std::cerr << "Orienting normals using scan direction flag only" << std::endl;
CGAL::scanline_orient_normals(points,CGAL::parameters::point_map(Point_map()).
normal_map(Normal_map()).
scanline_id_map(Scanline_id_map()));
dump("out_flag.ply", points);
// ---------------------使用扫描角度对点云进行法线定向-------------------------------
std::cerr << "Orienting normals using scan angle only" << std::endl;
CGAL::scanline_orient_normals(points,CGAL::parameters::point_map(Point_map()).
normal_map(Normal_map()).
scan_angle_map(Scan_angle_map()));
dump("out_angle.ply", points);
// -----------------------不使用额外信息的法线定向-----------------------------------
std::cerr << "Orienting normals using no additional info" << std::endl;
CGAL::scanline_orient_normals(points,CGAL::parameters::point_map(Point_map()).
normal_map(Normal_map()));
dump("out_nothing.ply", points);
return 0;
}