最近一段时间一直在忙论文,好在把论文大框架已经确定,后续就是细节上的修改以及创新。在下面的一段时间我会不定期的分享一下我论文中尝试过的各个方法的使用感。本篇重点介绍一下点云的聚类方法,我尝试了DBSCAN 聚类、区域增长聚类以及K-means聚类这三类聚类方法,下面依次进行总结。
1.DBSCAN---以点的密度大小进行聚类
1.1基本概念:
初始设定两个值:minPts以及半径r
核心对象:若一个点的r邻域内点的个数大于等于minPts,我们就称该点为一个核心对象
直接密度可达:若某点p在核心点q的邻域内,则称p-q直接可达
密度可达:若有一个点序列:q0,q1,q2,…,qk,对序列里任意两个相邻的点都是直接可达的,则称从q0到qk密度可达
1.2基本流程:
- 任意选择一个未被访问的点p,并将该点标记为已访问
- 如果p的邻域内点的个数大于mminPts(核心对象),则初始化一个簇C,将p以及p领域内的点加入到C中
- 遍历C中每个点,如果有未被访问的,将其标记为已访问。如果该点也是核心对象,则同样将该点邻域内的点加入到C中
- 重复步骤3直到C中不再存在没被访问的核心对象,将簇C加入到一个集合final中
- 重复步骤1234直到没有核心点未被标记,剩余的点标记为噪声点。
- 输出final与噪声点
1.3 核心代码1
def vector_distance_v2(v):
"""
把单个向量内部的每个元素两两相减,得到一个差值矩阵,矩阵是上三角和下三角刚好相反的结果
:param v: 可以是一个一维数组,或者一个一维的列表
:return:
"""
if type(v) is list:
v = np.array(v)
# result = []
# for i in range(len(v)):
# result.append(v[i] - v) # 可以改为列表推导式
result = [v[i] - v for i in range(len(v))]
return np.vstack(result)
def point_distance(points):
"""
计算所有 points 两两之间的距离
:param points: 地面分割之后检测出来的点 n * 4
:return: n * n 的距离矩阵
"""
d2 = vector_distance_v2(points[:,0])**2 + \
vector_distance_v2(points[:,1])**2 + \
vector_distance_v2(points[:,2])**2
return np.sqrt(d2)
# @profile
def DBSCAN_points(points, eps=2., Minpts=15):
"""
基于密度的点云聚类
:param d_bbox: 点与点之间的距离矩阵
:param eps: 最大搜索直径阈值
:param Minpts: 最小包含其他对象数量阈值
:return: 返回聚类结果,是一个嵌套列表,每个子列表就是这个区域的对象的序号
"""
# 先求距离
print('DBSCAN clustering:',points.shape)
d_bbox = point_distance(points)
#初始化核心对象集合T,聚类个数k,聚类集合C, 未访问集合P,
T = set()
k = 0
C = []
P = set(range(d_bbox.shape[0]))
# print('P',P)
for d in range(d_bbox.shape[0]):
# print(np.sum( d_bbox[d,:] <= eps))
if np.sum( d_bbox[d,:] <= eps) >= Minpts:
T.add(d) # 最初的核心对象
print('Len T: ',len(T))
#开