三维点云课程—ISS特征点
三维点云课程---ISS特征点
1.ISS流程
内部形态描述子(ISS)是一种表示立体几何形状的方法,具体流程如下:
1.对点云 P P P中每一个点 p i p_i pi建立一个局部坐标系,并对所有点设立一个搜索半径 r r r
2.对任意的点
p
j
p_j
pj,它的权重是
w
j
=
1
∣
{
p
k
:
∣
∣
p
k
−
p
j
∣
∣
2
<
r
}
∣
w_j=\frac{1}{|\{p_k:||p_k-p_j||_2<r\}|}
wj=∣{pk:∣∣pk−pj∣∣2<r}∣1
表示在邻居
r
r
r内点的个数
for idx_neighbor in np.asarray(idx_neighbors[1:]):
if not idx_neighbor in num_rnn_cache:
[k_, _, _] = search_tree.search_radius_vector_3d(points[idx_neighbor], radius)
num_rnn_cache[idx_neighbor] = k_
# 更新矩阵
w.append(num_rnn_cache[idx_neighbor])
deviation.append(points[idx_neighbor] - center)
其中 w w w表示权重,表示r领域内邻居点的个数
3.计算协方差矩阵
C
o
v
(
p
i
)
=
∑
∣
∣
p
i
−
p
j
∣
∣
2
<
r
w
j
(
p
j
−
p
i
)
(
p
j
−
p
i
)
T
∑
∣
∣
p
j
−
p
i
∣
∣
2
<
r
w
j
Cov(p_i)=\frac{{\sum\nolimits_{||{p_i} - {p_j}|{|_2} < r} {{w_j}({p_j} - {p_i}){{({p_j} - {p_i})}^T}} }}{{\sum\nolimits_{||{p_j} - {p_i}|{|_2} < r} {{w_j}} }}
Cov(pi)=∑∣∣pj−pi∣∣2<rwj∑∣∣pi−pj∣∣2<rwj(pj−pi)(pj−pi)T
w = np.asarray(w)
deviation = np.asarray(deviation)
#矩阵形式的计算 cov=D^T∑D/w.sum()
cov = (1.0 / w.sum()) * np.dot(deviation.T,np.dot(np.diag(w), deviation))
其实在上述程序中,对上述的协方差矩阵进行了从标量模式转矩阵模式
∑
∣
∣
p
i
−
p
j
∣
∣
2
<
r
w
j
(
p
j
−
p
i
)
(
p
j
−
p
i
)
T
=
∑
j
w
j
x
j
x
j
T
=
X
W
X
T
{\sum\nolimits_{||{p_i} - {p_j}|{|_2} < r} {{w_j}({p_j} - {p_i}){{({p_j} - {p_i})}^T}} }={\sum\nolimits_{j} {{w_j}x_j{{x_j}^T}} }=XWX^T
∑∣∣pi−pj∣∣2<rwj(pj−pi)(pj−pi)T=∑jwjxjxjT=XWXT
在上述程序中cov就是这样表示的,可能程序与理论的公式并不是完全对应的,因为公式中的
x
j
x_j
xj表示的
3
×
1
3\times 1
3×1的,而程序中deviation(np.asarray()之前的)表示的是
1
×
3
1 \times 3
1×3的。
4.计算 C o v ( p i ) Cov(p_i) Cov(pi)的特征值 λ i 1 , λ i 2 , λ i 3 \lambda_i^{1},\lambda_i^{2},\lambda_i^{3} λi1,λi2,λi3,并按照从大到小的顺序的排好序
#获得特征值值:
w, _ = np.linalg.eig(cov)
w = w[w.argsort()[::-1]]
5.对于特征值需要做些特殊的处理
- 对于一个平面的表面 λ i 1 = λ i 2 > λ i 3 \lambda_i^{1}=\lambda_i^{2}>\lambda_i^{3} λi1=λi2>λi3
- 对于一个线 λ i 1 > λ i 2 = λ i 3 \lambda_i^{1}>\lambda_i^{2}=\lambda_i^{3} λi1>λi2=λi3
- 因此我们需要 λ i 1 > λ i 2 > λ i 3 \lambda_i^{1}>\lambda_i^{2}>\lambda_i^{3} λi1>λi2>λi3,已排除线和面的可能性
df_point_eigen_values = df_point_eigen_values.loc[
(df_point_eigen_values['lambda_0'] > df_point_eigen_values['lambda_1']) &
(df_point_eigen_values['lambda_1'] > df_point_eigen_values['lambda_2']),
df_point_eigen_values.columns
]
6.需要进行非极大值抑制进行对多个 λ i 3 \lambda_i^{3} λi3进行处理
#通过点的邻居来确定非(NMS)的点
suppressed = set()
while pq:
_, idx_center = heapq.heappop(pq) #先弹首部的元素,对应于pq[0],在删除pq[0]
if not idx_center in suppressed:
[_, idx_neighbors, _] = search_tree.search_radius_vector_3d(points[idx_center], radius)
for idx_neighbor in np.asarray(idx_neighbors[1:]):
suppressed.add(idx_neighbor)
else:
continue
df_point_eigen_values = df_point_eigen_values.loc[df_point_eigen_values['id'].apply(lambda id: not id in suppressed),df_point_eigen_values.columns]
在使用非极大值抑制(NMS)的程序中先通过点的邻居来确定非NMS的点,在通过程序的not来挑选NMS后的点。
2.ISS完整代码即流程
# detect-iss.py
# 1. load point cloud in modelnet40 normal format
# 2. calculate ISS keypoints
# 3. visualize the results
import argparse
import numpy as np
import pandas as pd
import open3d as o3d
import heapq #堆heapd的使用
#txt数据类型为x,y,z,nx,ny,nz
def read_modelnet40_normal(filepath):
# load data:
df_point_cloud_with_normal = pd.read_csv(
filepath, header=None
)
# add colunm names:
df_point_cloud_with_normal.columns = [
'x', 'y', 'z',
'nx', 'ny', 'nz'
]
pcd = o3d.geometry.PointCloud()
pcd.points = o3d.utility.Vector3dVector(
df_point_cloud_with_normal[['x', 'y', 'z']].values
)
pcd.normals = o3d.utility.Vector3dVector(
df_point_cloud_with_normal[['nx', 'ny', 'nz']].values
)
return pcd
def get_arguments():
"""
Get command-line arguments
"""
# init parser:
parser = argparse.ArgumentParser("Detect ISS keypoints on ModelNet40 dataset.")
# add required and optional groups:
required = parser.add_argument_group('Required')
optional = parser.add_argument_group('Optional')
# add required:
required.add_argument(
"-i", dest="input", help="Input path of ModelNet40 sample.",
required=True
)
required.add_argument(
"-r", dest="radius", help="Radius for radius nearest neighbor definition.",
required=True, type=float
)
# parse arguments:
return parser.parse_args()
if __name__ == '__main__':
#参数输入
#arguments = get_arguments()
#input_filename = arguments.input
#radius = arguments.radius
input_filename="Airplane_0001.txt"
radius=0.1
#加载点云
pcd = read_modelnet40_normal(input_filename)
#建立搜索树
search_tree = o3d.geometry.KDTreeFlann(pcd)
points = np.asarray(pcd.points)
print("原始点云数据的个数:",points.shape[0])
df_point_eigen_values = {'id': [],'lambda_0': [],'lambda_1': [],'lambda_2': []}
# num rnn cache:
num_rnn_cache = {}
# 构建用于非极大值抑制的堆:
pq = []
for idx_center, center in enumerate(points):
# 寻找radius的邻居,k表示邻居的个数,idx_neighbors表示各邻居的索引
[k, idx_neighbors, _] = search_tree.search_radius_vector_3d(center, radius)
## 对每一个点进行领域搜索:
w = [] #权重矩阵
deviation = [] #距离矩阵
for idx_neighbor in np.asarray(idx_neighbors[1:]):
if not idx_neighbor in num_rnn_cache:
[k_, _, _] = search_tree.search_radius_vector_3d(points[idx_neighbor], radius)
num_rnn_cache[idx_neighbor] = k_
# 更新矩阵
w.append(num_rnn_cache[idx_neighbor])
deviation.append(points[idx_neighbor] - center)
# 将[]转成numpy的形式
w = np.asarray(w)
deviation = np.asarray(deviation)
#矩阵形式的计算 cov=D^T∑D/w.sum()
cov = (1.0 / w.sum()) * np.dot(deviation.T,np.dot(np.diag(w), deviation))
#获得特征值值:
w, _ = np.linalg.eig(cov)
w = w[w.argsort()[::-1]]
# 将最小特征值压入堆里,会进行自动排序,小的(-w[2])放在前面:
heapq.heappush(pq, (-w[2], idx_center))
# 将数据保存到字典里
df_point_eigen_values['id'].append(idx_center)
df_point_eigen_values['lambda_0'].append(w[0])
df_point_eigen_values['lambda_1'].append(w[1])
df_point_eigen_values['lambda_2'].append(w[2])
#通过点的邻居来确定非(NMS)的点
suppressed = set()
while pq:
_, idx_center = heapq.heappop(pq) #先弹首部的元素,对应于pq[0],在删除pq[0]
if not idx_center in suppressed:
# suppress its neighbors:
[_, idx_neighbors, _] = search_tree.search_radius_vector_3d(points[idx_center], radius)
for idx_neighbor in np.asarray(idx_neighbors[1:]):
suppressed.add(idx_neighbor)
else:
continue
print ("非NMS的个数:",len(suppressed))
# 将字典变成pandas模式
df_point_eigen_values = pd.DataFrame.from_dict(df_point_eigen_values)
# 首先通过非极大值抑制进行刷选,not是核心
df_point_eigen_values = df_point_eigen_values.loc[df_point_eigen_values['id'].apply(lambda id: not id in suppressed),df_point_eigen_values.columns]
# 然后通过特征值的变化进行刷选,其实这部可以不加,df已经是按照特征值的从大到小进行排列了
df_point_eigen_values = df_point_eigen_values.loc[
(df_point_eigen_values['lambda_0'] > df_point_eigen_values['lambda_1']) &
(df_point_eigen_values['lambda_1'] > df_point_eigen_values['lambda_2']),
df_point_eigen_values.columns
]
print("ISS得到的特征点个数:",len(df_point_eigen_values['id']))
# 非特征点的颜色 灰色
pcd.paint_uniform_color([0.95, 0.95, 0.95])
# 特征点颜色,红色
np.asarray(pcd.colors)[df_point_eigen_values['id'].values, :] = [1.0, 0.0, 0.0]
o3d.visualization.draw_geometries([pcd])
代码参考:大佬的代码
数据集下载:
Airplane_0001.txt 密码:7875
car_0001.txt 密码:7875
chair_0001.txt 密码 :7875
更多的数据集需要下载请加我的QQ:2822908208
仿真效果
Airplane r=0.05
Airplane r=0.1