CGAL GIS 应用 - 从点云到DTM

CGAL GIS 应用 - 从点云到DTM

GIS应用中使用的许多传感器(例如激光雷达)都会生成密集的点云。此类应用通常利用更高级的数据结构:例如,不规则三角网(TIN),它可以作为数字高程模型(DEM)的基础,特别是用于生成数字地形模型(DTM)。
点云也可以通过分类信息来丰富,分类信息将点分割为地面、植被和建筑物点(或其他用户定义的标签)

某些数据结构的定义可能因来源不同而有所不同。在本文中使用以下术语:

  • TIN:三角不规则网络,一种二维三角结构,根据它们在水平面上的投影连接3D点。
  • DSM:数字表面模型,包括建筑物和植被的整个扫描表面的模型。我们用一个TIN来存储DSM
  • DTM:数字地形模型,一种没有建筑物或植被等物体的裸地表面模型。我们都使用TIN和光栅来存储DTM
  • DEM:数字高程模型,一个更通用的术语,包括DSM和DTM`。

本文演示了以下场景。从输入点云,我们首先计算存储为TINDSM。然后,我们过滤掉与建筑立面或植被噪声相对应的过大的面片。保留与地面相对应的大型构件。对孔洞进行填充,并对得到的DEM重新网格化。由栅格DEM生成一组等高线折线。最后,对植被、建筑物和分组点进行有监督的三标签分类。

不规则三角网(TIN)

CGAL提供了多种三角网数据结构和算法。TIN可以通过将二维Delaunay三角剖分与投影特性相结合来生成:三角剖分结构是利用点沿选定平面(通常是xy平面)的二维位置来计算的,而点的三维位置则保留以供可视化和测量。

Digital Surface Model (DSM)

使用表示输入输出流stream操作符,可以很容易地将许多格式(XYZ、OFF、PLY、LAS)的点云加载到 CGAL::Point_set_3结构中。生成存储在TIN中的DSM很简单。

由于CGAL的Delaunay三角剖分是一个 FaceGraph 的模型,因此可以直接将生成的TIN转换为 CGAL::Surface_mesh 这样的网状结构,并保存为该结构支持的任何格式。

#include<iostream>
#include<CGAL/Surface_mesh.h>
#include<CGAL/Surface_mesh/IO/PLY.h>

#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Projection_traits_xy_3.h>
#include <CGAL/Delaunay_triangulation_2.h>

#include <CGAL/boost/graph/graph_traits_Delaunay_triangulation_2.h>
#include <CGAL/boost/graph/copy_face_graph.h>

#include <CGAL/Point_set_3.h>
#include <CGAL/Point_set_3/IO.h>


using Kernel = CGAL::Exact_predicates_inexact_constructions_kernel;
using Projection_traits = CGAL::Projection_traits_xy_3<Kernel>;
using Point_2 = Kernel::Point_2;
using Point_3 = Kernel::Point_3;

// Triangulated Irregular Network
using TIN = CGAL::Delaunay_triangulation_2<Projection_traits>;
using Mesh = CGAL::Surface_mesh<Point_3>;

int main() {
    std::ifstream ifile("points.xyz", std::ios_base::binary);
    CGAL::Point_set_3<Point_3> points;
    ifile >> points;
    std::cerr << points.size() << " point(s) read" << std::endl;
    // Create DSM
    TIN dsm(points.points().begin(), points.points().end());
    // Write DSM
    Mesh dsm_mesh;
    CGAL::copy_face_graph(dsm, dsm_mesh);
    std::ofstream dsm_ofile("dsm.ply", std::ios_base::binary);
    CGAL::IO::set_binary_mode(dsm_ofile);
    CGAL::IO::write_PLY(dsm_ofile, dsm_mesh);
    dsm_ofile.close();
    return 0;
}

输入points.xyz
在这里插入图片描述
输出DSM ply文件:
在这里插入图片描述
注:由密集点云生成三角网格Mesh,可以使用GIS等软件如QGIS TIN Mesh Creation工具,
参考:QGIS 高程点生成Mesh

官方示例:b9_training.ply点云数据生成DSM

  const std::string fname = argc != 2 ? CGAL::data_file_path("../data/points_3/b9_training.ply") : argv[1];
  if (argc != 2)
  {
    std::cerr << "Usage: " << argv[0] << " points.ply" << std::endl;
    std::cerr << "Running with default value " << fname << "\n";
  }
  // Read points
  std::ifstream ifile (fname, std::ios_base::binary);
  CGAL::Point_set_3<Point_3> points;
  ifile >> points;
  std::cerr << points.size() << " point(s) read" << std::endl;
  // Create DSM
  TIN dsm (points.points().begin(), points.points().end());
  using Mesh = CGAL::Surface_mesh<Point_3>;
  Mesh dsm_mesh;
  CGAL::copy_face_graph (dsm, dsm_mesh);
  std::ofstream dsm_ofile ("dsm.ply", std::ios_base::binary);
  CGAL::IO::set_binary_mode (dsm_ofile);
  CGAL::IO::write_PLY (dsm_ofile, dsm_mesh);
  dsm_ofile.close();

在这里插入图片描述

数字地形模型(DTM)

生成的DSM被用作DTM计算的基础,即地面表示为过滤掉非地面点后的另一个TIN
本文提出一个简单的DTM估计方法,分解如下步骤:

  1. 对面片的高度进行阈值化,以消除高度的剧烈变化;
  2. 将其他面片聚类为连通分支;
  3. 过滤所有小于用户定义阈值的组件。
    该算法依赖于两个参数:一个高度阈值对应于建筑物的最小高度,一个周长阈值对应于二维投影上建筑物的最大尺寸。

CGAL从DSM到DTM区域提取
coloredTIN
CGAL从DSM到DTM filtering
Filtering

CGAL从DSM到DTM 孔洞填充及网格细化
DTM

栅格化

TIN数据结构可以与重心坐标相结合,以便插值并栅格化顶点中嵌入的任何信息所需的任何分辨率的高度图。

示例参见:

  1. Mesh 网格曲面栅格化
  2. 虚幻地形高度图生成及测试

生成栅格PPM
在这里插入图片描述
生成栅格PNG:
在这里插入图片描述

提取等高线

提取TIN上定义的函数的等值层是另一个可以使用CGAL完成的应用。本文演示如何提取等值高度来构建地形图。

步骤:

  1. 构建等高线图Graph
  2. Graph分裂为多折线Polyline;
  3. Polyline简化。

详细示例及代码参见:

Mesh地形曲面提取等高线

在这里插入图片描述

Classifying 分类

CGAL提供了一个包分类,可用于将点云分割为用户定义的标签集。目前CGAL中可用的最先进的分类器是ETHZ中的随机森林。由于它是一个监督分类器,因此需要一个训练集。
下面的代码片段展示了如何使用一些手动选择的训练集来训练随机森林分类器,并计算通过图割算法正则化的分类:

// Get training from input
  Point_set::Property_map<int> training_map;
  bool training_found;
  std::tie (training_map, training_found) = points.property_map<int>("training");
  if (training_found)
  {
    std::cerr << "Classifying ground/vegetation/building" << std::endl;
    // Create labels
    Classification::Label_set labels ({ "ground", "vegetation", "building" });
    // Generate features
    Classification::Feature_set features;
    Classification::Point_set_feature_generator<Kernel, Point_set, Point_set::Point_map>
      generator (points, points.point_map(), 5); // 5 scales
#ifdef CGAL_LINKED_WITH_TBB
    // If TBB is used, features can be computed in parallel
    features.begin_parallel_additions();
    generator.generate_point_based_features (features);
    features.end_parallel_additions();
#else
    generator.generate_point_based_features (features);
#endif
    // Train a random forest classifier
    Classification::ETHZ::Random_forest_classifier classifier (labels, features);
    classifier.train (points.range(training_map));
    // Classify with graphcut regularization
    Point_set::Property_map<int> label_map = points.add_property_map<int>("labels").first;
    Classification::classify_with_graphcut<Concurrency_tag>
      (points, points.point_map(), labels, classifier,
       generator.neighborhood().k_neighbor_query(12), // regularize on 12-neighbors graph
       0.5f, // graphcut weight
       12, // Subdivide to speed-up process
       label_map);
    // Evaluate
    std::cerr << "Mean IoU on training data = "
              << Classification::Evaluation(labels,
                                            points.range(training_map),
                                            points.range(label_map)).mean_intersection_over_union() << std::endl;
    // Save the classified point set
    std::ofstream classified_ofile ("classification_gis_tutorial.ply");
    CGAL::IO::set_binary_mode (classified_ofile);
    classified_ofile << points;
    classified_ofile.close();
  }

在这里插入图片描述

参考及相关链接

  1. https://doc.cgal.org/latest/Manual/tuto_gis.html
你可以使用CGAL和PCL库来进行带有法线的点云数据的转换。下面是一个简单的示例代码,展示了如何将CGAL中的点云数据转换为PCL库中的PointNormal类型的点云数据: ```cpp #include <CGAL/Exact_predicates_inexact_constructions_kernel.h> #include <CGAL/Point_with_normal_3.h> #include <CGAL/IO/read_xyz_points.h> #include <CGAL/property_map.h> #include <CGAL/IO/write_ply_points.h> #include <pcl/point_types.h> #include <pcl/io/pcd_io.h> #include <pcl/io/ply_io.h> #include <pcl/point_cloud.h> #include <pcl/features/normal_3d.h> typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel; typedef Kernel::Point_3 Point_3; typedef CGAL::Point_with_normal_3<Kernel> Point_with_normal_3; typedef std::vector< Point_with_normal_3 > PointList; int main() { // 读取带有法线的点云数据 PointList points; std::ifstream stream("input.xyz"); if (!stream || !CGAL::read_xyz_points(stream, std::back_inserter(points), CGAL::parameters::point_map(CGAL::First_of_pair_property_map<Point_with_normal_3>()). normal_map(CGAL::Second_of_pair_property_map<Point_with_normal_3>()))) { std::cerr << "Error: cannot read file!" << std::endl; return 1; } // 将点云数据转换为PCL库中的PointNormal类型的点云数据 pcl::PointCloud<pcl::PointNormal>::Ptr pcl_cloud(new pcl::PointCloud<pcl::PointNormal>); for (const auto& point : points) { pcl::PointNormal pcl_point; pcl_point.x = point.x(); pcl_point.y = point.y(); pcl_point.z = point.z(); pcl_point.normal_x = point.normal().x(); pcl_point.normal_y = point.normal().y(); pcl_point.normal_z = point.normal().z(); pcl_cloud->push_back(pcl_point); } // 保存PCL点云数据 pcl::io::savePLYFile("output.ply", *pcl_cloud); return 0; } ``` 上述代码将从"input.xyz"文件中读取带有法线的点云数据,并将其转换为PCL库中的PointNormal类型的点云数据。最后,将转换后的点云数据保存为"output.ply"文件。你可以根据自己的需求修改文件路径和名称。 请确保已正确安装和配置CGAL和PCL库,并将相关头文件和库文件包含到你的项目中。这样,你就可以使用上述示例代码将CGAL中的点云数据转换为PCL库中的PointNormal类型的点云数据了。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值