(五) 三维点云课程---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:pkpj2<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)=pjpi2<rwjpipj2<rwj(pjpi)(pjpi)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 pipj2<rwj(pjpi)(pjpi)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
在这里插入图片描述

  • 5
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 15
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 15
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值