Wolfram Mathematica 三维点云进阶篇——点云分割

0 阅读4分钟

MMA点云进阶篇——点云分割篇


前言

随着深度学习的兴起,各种复杂的分割方法如雨后春笋般兴起。但是不应忽视传统方法的基石作用,在某些情况,传统方法在分割上的效果往往好于基于深度学习方法,这已经被许多研究证明。借此点云分割,一起学习和掌握在MMA中使用各种聚类方法。

一、聚类方法介绍

主要介绍FindClusters函数的用法,在MMA中,FindClusters函数支持如下的方法:

  1. 其中"KMeans" 和 "KMedoids" 需要显示指定划分的簇数;
  2. "DBSCAN"、"JarvisPatrick"、"MeanShift"、"SpanningTree"、 "NeighborhoodContraction" 和 "GaussianMixture" 不用指定划分簇数,会自动寻找可划分的簇数;
  3. "Agglomerate"、"Spectral" 和 "SpanningTree" 方法两种情况下都可以

对于后续的聚类案例,我会使用Meanshift举例。

二、Meanshift聚类介绍

The "MeanShift" method iteratively shifts data points toward higher-density regions

Meanshift是一种基于密度的非参数聚类算法,上面这句话,说明了其核心思想,就是让每个数据点沿着密度增加的方向“漂移”,最终汇聚到密度分布的局部极大值点。而局部的极大值点就代表着簇中心,有多少个这样点,就有多少簇。

1、算法步骤

  1. 初始化:将每个数据点视为一个潜在的中心。
  2. 迭代漂移:对于每个点 ,计算其邻域内所有点的加权平均位置 ,然后将点移动到该位置:(其中 K 是核函数,通常采用高斯核)

  1. 收敛判断:重复上述步骤,直到所有点的移动距离小于某个容差(即点不再显著变化)

三、案例实现

选择个一个开放的UAV-Lidar数据集(https://figshare.com/s/9823af091bb401eea612. 但是目前该连接似乎不好用了),该数据集来自纽卡斯尔大学农场内的树木。可视化如下: ​

如果直接对三维点云应用聚类,结果十分糟糕,这是因为树冠与树干的特点截然不同,尤其是点云密度,因为该数据来自UAV,从树冠顶部到树干底部的点云密度逐渐减少。

直接聚类代码

(*filteredPoints代表已经处理后的点云*)
clusters = FindClusters[filteredPoints, 
   Method -> {"MeanShift", "NeighborhoodRadius" -> 0.2}];

filteredPoints = Catenate[clusters];

randomColors = RandomColor[Length[clusters]];(*创建颜色*)

coloredClusters = MapThread[{#1, Point[#2]} &, {randomColors, clusters}];

Graphics3D[{PointSize[0.01], coloredClusters}, Axes -> True, 
 PlotLabel -> "Clusters by Color"]

直接聚类结果

由于从上到下的点云密度不均匀,直接聚类不可行。考虑树木还具有垂直方向的整体性,即一个树干对应一个树冠。因此尝试对XOY平面应用Meanshift聚类,随后将聚类结果映射到三维点云。

水平聚类代码

pointsXY = filteredPoints[[All, {1, 2}]];

pointsXY = filteredPoints[[All, {1, 2}]];

(*Meanshift聚类*)
clusters = 
  FindClusters[pointsXY, 
   Method -> {"MeanShift", "NeighborhoodRadius" -> 0.12}];

Print["簇的数量: ", Length[clusters]];


(*用 ListPlot 绘制*)
If[clusters =!= {}, ListPlot[clusters, PlotStyle -> PointSize[0.01], 
  AspectRatio -> Automatic, 
  PlotLabel -> "DBSCAN Clusters (不同颜色代表不同簇)"], 
 Print["没有找到任何簇(全部为噪声)"]]

聚类结果映射

这段代码中,最难的就是映射到三维。需要分开看在Table[]里面有一个 nf[#, {1, tolerance}] & /@ cluster,其含义是对于平面聚类后的结果cluster(pointsXY)在三维点云(filteredPoints)找到最近的一点。

后面的 * //Flatten[#,1] *其主要作用是展平列表,可以理解为去除一层括号。因为,在查找后,点会变成这样 :{ {{x1,y1,z1}}, {{x2,y2,z2}} } ,三层括号,在后续可视化会出现问题,于是要去掉一层,其中 // 是后缀操作符,将左边的结果作为参数传递给右边的函数。

这里还有一个Table[]用法,对于Table[....... , {cluster, clusters}]表示,遍历clusters的每一个元素,执行省略号里的规则,并将当前元素赋值给变量cluster。

(*Nearest 函数在初始点云中映射*)
nf = Nearest[pointsXY -> filteredPoints];

(*容差*)
tolerance = 10^-4;

(*将二维簇映射到三维簇*)
clusters3D = Table[[nf[#, {1, tolerance}] & /@ cluster] // Flatten[#, 1] &,
    {cluster, clusters}];

(*移除可能为空的簇(如果某个簇全部未匹配)*)
clusters3D = DeleteCases[clusters3D, {}];

(*可视化*)
randomColors = RandomColor[Length[clusters3D]];
coloredClusters = MapThread[{#1, Point[#2]} &, {randomColors, clusters3D}];

Graphics3D[{PointSize[0.01], coloredClusters}, Axes -> True, 
 BoxRatios -> Automatic, PlotLabel -> "3D Clusters (from 2D)"]

下面就是最终的可视化结果,可见分割效果有了明显提升。

​​

​​

总结                 

借此点云分割,介绍在MMA进行聚类的方法,并以Meanshift为例子,展示具体操作。后续还有更深入的实现,比如,通过一些监督学习的方法能够改进分割的精度,并在更复杂的场景下分割点云。


以上就是本文全部内容,后面将继续探索MMA在三维点云领域的应用,为MMA的中文资料继续添砖加瓦。