pointcloud_homework2

task

for one 𝑁 × 3 point cloud from KITTI
8-NN search for each point to the point cloud
Implement 3 NN algorithms

  • Numpy brute-force search
  • scipy.spatial.KDTree
  • own kd-tree in python

codes revised from(代码的借鉴来源) https://github.com/lijx10/NN-Trees.

solution

在google colab上完成并运行

1.读取二进制数据文件

文件为KITTI数据集中的.bin二进制数据,需要读取raw data的激光雷达的三维坐标信息

import os 
import struct
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
def read_velodyne_bin(path):
    '''
    :param path:
    :return: homography matrix of the point cloud, N*3
    '''
    pc_list = []
    with open(path, 'rb') as f:
        content = f.read()
        pc_iter = struct.iter_unpack('ffff', content)
        for idx, point in enumerate(pc_iter):
            pc_list.append([point[0], point[1], point[2]])
    return np.asarray(pc_list, dtype=np.float32)

path='/content/drive/My Drive/lesson2code/000000.bin'
origindata=read_velodyne_bin(path)

2.构造一种可记录并判断N个最近邻的容器的函数


import copy


class DistIndex:
    def __init__(self, distance, index):
        self.distance = distance
        self.index = index

    def __lt__(self, other):
        return self.distance < other.distance


class KNNResultSet:
    def __init__(self, capacity):
        self.capacity = capacity
        self.count = 0
        self.worst_dist = 1e10
        self.dist_index_list = []
        for i in range(capacity):
            self.dist_index_list.append(DistIndex(self.worst_dist, 0))

        self.comparison_counter = 0

    def size(self):
        return self.count

    def full(self):
        return self.count == self.capacity

    def worstDist(self):
        return self.worst_dist

    def add_point(self, dist, index):
        self.comparison_counter += 1
        if dist > self.worst_dist:
            return

        if self.count < self.capacity:
            self.count += 1

        i = self.count - 1
        while i > 0:
            if self.dist_index_list[i-1].distance > dist:
                self.dist_index_list[i] = copy.deepcopy(self.dist_index_list[i-1])
                i -= 1
            else:
                break

        self.dist_index_list[i].distance = dist
        self.dist_index_list[i].index = index
        self.worst_dist = self.dist_index_list[self.capacity-1].distance
        
    def __str__(self):
        output = ''
        for i, dist_index in enumerate(self.dist_index_list):
            output += '%d - %.2f\n' % (dist_index.index, dist_index.distance)
        output += 'In total %d comparison operations.' % self.comparison_counter
        return output



    

3.numpy暴力搜索法

import random
import math
import numpy as np
import time

def bruteSearch(db: np.ndarray,result_set:KNNResultSet, query: np.ndarray):
    
    diff = np.linalg.norm(np.expand_dims(query, 0) - db, axis=1)
    nn_idx = np.argsort(diff)
    nn_dist = diff[nn_idx]
    for idx, distindex in enumerate(result_set.dist_index_list):
        distindex.distance=nn_dist[idx]
        distindex.index=nn_idx[idx]
    return False
k=8
brute_time_sum=0
for ind ,p in enumerate(origindata):
    begin_t = time.time()
    result_set = KNNResultSet(capacity=k)
    bruteSearch(origindata,result_set,p)
    brute_time_sum += time.time() - begin_t
print('brute:',brute_time_sum)

使用colab里的GPU运行上述片断,耗时1348.81

4.scipy的kdtree模块

from scipy import spatial
import time

k=8

def scipyKdtreeSearch(tree:spatial.KDTree,result_set:KNNResultSet,point: np.ndarray):
    scipy_nn_dis,scipy_nn_idx=tree.query(point,result_set.capacity)
    for idx, distindex in enumerate(result_set.dist_index_list):
        distindex.distance=scipy_nn_dis[idx]
        distindex.index=scipy_nn_idx[idx]
    return False


construction_time_sum = 0
knn_time_sum = 0

begin_t = time.time()
tree = spatial.KDTree(origindata)
construction_time_sum += time.time() - begin_t

for ind ,p in enumerate(origindata):
    begin_t = time.time()
    result_set = KNNResultSet(capacity=k)
    scipyKdtreeSearch(tree,result_set,p)
    knn_time_sum += time.time() - begin_t
    
print('scipykdtreebuilding:',construction_time_sum)
print('scipykdtree:',knn_time_sum)
使用colab里的GPU运行上述片断,建树与查找共耗时114.28

5.kdtree的python实现1

此情况为切割位置没有点的kdtree实现

在这里插入图片描述


# kdtree的具体实现,包括构建和查找

import random
import math
import numpy as np
import time


# Node类,Node是tree的基本组成元素
class Node:
    def __init__(self, axis, value, left, right, point_indices):
        self.axis = axis
        self.value = value
        self.left = left
        self.right = right
        self.point_indices = point_indices

    def is_leaf(self):
        if self.value is None:
            return True
        else:
            return False

    def __str__(self):
        output = ''
        output += 'axis %d, ' % self.axis
        if self.value is None:
            output += 'split value: leaf, '
        else:
            output += 'split value: %.2f, ' % self.value
        output += 'point_indices: '
        output += str(self.point_indices.tolist())
        return output

# 功能:构建树之前需要对value进行排序,同时对一个的key的顺序也要跟着改变
# 输入:
#     key:键
#     value:值
# 输出:
#     key_sorted:排序后的键
#     value_sorted:排序后的值
def sort_key_by_vale(key, value):
    assert key.shape == value.shape
    assert len(key.shape) == 1
    sorted_idx = np.argsort(value)
    key_sorted = key[sorted_idx]
    value_sorted = value[sorted_idx]
    return key_sorted, value_sorted


def axis_round_robin(axis, dim):
    if axis == dim-1:
        return 0
    else:
        return axis + 1

# 功能:通过递归的方式构建树
# 输入:
#     root: 树的根节点
#     db: 点云数据
#     point_indices:排序后的键
#     axis: scalar
#     leaf_size: scalar
# 输出:
#     root: 即构建完成的树
def kdtree_recursive_build(root, db, point_indices, axis, leaf_size):
    if root is None:
        root = Node(axis, None, None, None, point_indices)

    # determine whether to split into left and right
    if len(point_indices) > leaf_size:
        # --- get the split position ---
        point_indices_sorted, _ = sort_key_by_vale(point_indices, db[point_indices, axis])  # M
        
        # 作业1
        # 屏蔽开始
        middle_left_idx = math.ceil(point_indices_sorted.shape[0] / 2) - 1
        middle_left_point_idx = point_indices_sorted[middle_left_idx]
        middle_left_point_value = db[middle_left_point_idx, axis]
        middle_right_idx = middle_left_idx + 1
        middle_right_point_idx = point_indices_sorted[middle_right_idx]
        middle_right_point_value = db[middle_right_point_idx, axis]
        root.value = (middle_left_point_value + middle_right_point_value) * 0.5


        root.left=kdtree_recursive_build(root.left,db,point_indices_sorted[0:middle_right_idx],axis_round_robin(axis, dim=db.shape[1]),leaf_size)
        root.right=kdtree_recursive_build(root.right,db,point_indices_sorted[middle_right_idx:],axis_round_robin(axis, dim=db.shape[1]),leaf_size)
        # 屏蔽结束
    return root


# 功能:翻转一个kd树
# 输入:
#     root:kd树
#     depth: 当前深度
#     max_depth:最大深度
def traverse_kdtree(root: Node, depth, max_depth):
    depth[0] += 1
    if max_depth[0] < depth[0]:
        max_depth[0] = depth[0]

    if root.is_leaf():
        print(root)
    else:
        traverse_kdtree(root.left, depth, max_depth)
        traverse_kdtree(root.right, depth, max_depth)

    depth[0] -= 1

# 功能:构建kd树(利用kdtree_recursive_build功能函数实现的对外接口)
# 输入:
#     db_np:原始数据
#     leaf_size:scale
# 输出:
#     root:构建完成的kd树
def kdtree_construction(db_np, leaf_size):
    N, dim = db_np.shape[0], db_np.shape[1]

    # build kd_tree recursively
    root = None
    root = kdtree_recursive_build(root,db_np,np.arange(N),axis=0,leaf_size=leaf_size)
    return root


# 功能:通过kd树实现knn搜索,即找出最近的k个近邻
# 输入:
#     root: kd树
#     db: 原始数据
#     result_set:搜索结果
#     query:索引信息
# 输出:
#     搜索失败则返回False
def kdtree_knn_search(root: Node, db: np.ndarray, result_set: KNNResultSet, query: np.ndarray):
    if root is None:
        return False

    if root.is_leaf():
        # compare the contents of a leaf
        leaf_points = db[root.point_indices, :]
        diff = np.linalg.norm(np.expand_dims(query, 0) - leaf_points, axis=1)
        for i in range(diff.shape[0]):
            result_set.add_point(diff[i], root.point_indices[i])
        return False

    # 作业2
    # 提示:仍通过递归的方式实现搜索
    # 屏蔽开始
    if query[root.axis]<=root.value:
        kdtree_knn_search(root.left,db,result_set,query)
        if math.fabs(query[root.axis]-root.value)<result_set.worstDist():
            kdtree_knn_search(root.right,db,result_set,query)
    else:
        kdtree_knn_search(root.right,db,result_set,query)
        if math.fabs(query[root.axis]-root.value)<result_set.worstDist():
            kdtree_knn_search(root.left,db,result_set,query)
    

    # 屏蔽结束

    return False

def main():
    # configuration
    leaf_size = 10
    k = 8

    db_np = origindata
    construction_time_sum = 0
    begin_t = time.time()
    root = kdtree_construction(db_np, leaf_size=leaf_size)
    construction_time_sum += time.time() - begin_t
    
    # depth = [0]
    # max_depth = [0]
    # traverse_kdtree(root, depth, max_depth)
    # print("tree max depth: %d" % max_depth[0])


    
    knn_time_sum = 0
    for ind ,p in enumerate(origindata):
        begin_t = time.time()
        result_set = KNNResultSet(capacity=k)
        kdtree_knn_search(root, db_np, result_set, p)
        knn_time_sum += time.time() - begin_t
        
    print('kdtreebuilding:',construction_time_sum)
    print('kdtree:',knn_time_sum)
    


if __name__ == '__main__':
    main()


使用colab的GPU建树与查找共耗时199.05

6.kdtree的python实现2

此情况为切割位置有点的kdtree实现
在这里插入图片描述


# kdtree的修改的具体实现,包括构建和查找

import random
import math
import numpy as np
import time


# Node类,Node是tree的基本组成元素
class Node:
    def __init__(self, axis, valueindex, left, right, point_indices):
        self.axis = axis
        self.valueindex = valueindex
        self.left = left
        self.right = right
        self.point_indices = point_indices

    def is_leaf(self):
        if len(self.point_indices)==0:
            return True
        else:
            return False

    def __str__(self):
        output = ''
        output += 'axis %d, ' % self.axis
        if self.valueindex is None:
            output += 'split value: leaf, '
        else:
            output +=( 'split valueindex: ' + str(self.valueindex))
        output += 'point_indices: '
        output += str(self.point_indices.tolist())
        return output

# 功能:构建树之前需要对value进行排序,同时对一个的key的顺序也要跟着改变
# 输入:
#     key:键
#     value:值
# 输出:
#     key_sorted:排序后的键
#     value_sorted:排序后的值
def sort_key_by_vale(key, value):
    assert key.shape == value.shape
    assert len(key.shape) == 1
    sorted_idx = np.argsort(value)
    key_sorted = key[sorted_idx]
    value_sorted = value[sorted_idx]
    return key_sorted, value_sorted


def axis_round_robin(axis, dim):
    if axis == dim-1:
        return 0
    else:
        return axis + 1

# 功能:通过递归的方式构建树
# 输入:
#     root: 树的根节点
#     db: 点云数据
#     point_indices:排序后的键
#     axis: scalar
#     leaf_size: scalar
# 输出:
#     root: 即构建完成的树
def kdtree_recursive_build(root, db, point_indices, axis, leaf_size):
    if root is None:
        root = Node(axis, None, None, None, point_indices)

    # determine whether to split into left and right
    if len(point_indices) > leaf_size:
        # --- get the split position ---
        point_indices_sorted, _ = sort_key_by_vale(point_indices, db[point_indices, axis])  # M
        middle_left_idx = math.ceil(point_indices_sorted.shape[0] / 2) - 1
        middle_left_point_idx = point_indices_sorted[middle_left_idx]
        middle_right_idx = middle_left_idx + 1
        
        root.valueindex=middle_left_point_idx
        
        root.left=kdtree_recursive_build(root.left,db,point_indices_sorted[0:middle_left_idx],axis_round_robin(axis, dim=db.shape[1]),leaf_size)
        root.right=kdtree_recursive_build(root.right,db,point_indices_sorted[middle_right_idx:],axis_round_robin(axis, dim=db.shape[1]),leaf_size)
        
        
    return root


# 功能:翻转一个kd树
# 输入:
#     root:kd树
#     depth: 当前深度
#     max_depth:最大深度
def traverse_kdtree(root: Node, depth, max_depth):
    depth[0] += 1
    if max_depth[0] < depth[0]:
        max_depth[0] = depth[0]

    if root.is_leaf():
        print(root)
    else:
        traverse_kdtree(root.left, depth, max_depth)
        traverse_kdtree(root.right, depth, max_depth)

    depth[0] -= 1

# 功能:构建kd树(利用kdtree_recursive_build功能函数实现的对外接口)
# 输入:
#     db_np:原始数据
#     leaf_size:scale
# 输出:
#     root:构建完成的kd树
def kdtree_construction(db_np, leaf_size):
    N, dim = db_np.shape[0], db_np.shape[1]

    # build kd_tree recursively
    root = None
    root = kdtree_recursive_build(root,db_np,np.arange(N),axis=0,leaf_size=leaf_size)
    return root


# 功能:通过kd树实现knn搜索,即找出最近的k个近邻
# 输入:
#     root: kd树
#     db: 原始数据
#     result_set:搜索结果
#     query:索引信息
# 输出:
#     搜索失败则返回False
def kdtree_knn_search(root: Node, db: np.ndarray, result_set: KNNResultSet, query: np.ndarray):
    if root is None:
        return False
            
    diff=np.linalg.norm(query-db[root.valueindex])
    
    result_set.add_point(diff, root.valueindex)
    
    if root.is_leaf():
        return False

    
    if query[root.axis]<=db[root.valueindex][root.axis]:
        kdtree_knn_search(root.left,db,result_set,query)
        if math.fabs(query[root.axis]-db[root.valueindex][root.axis])<result_set.worstDist():
            kdtree_knn_search(root.right,db,result_set,query)
    else:
        kdtree_knn_search(root.right,db,result_set,query)
        if math.fabs(query[root.axis]-db[root.valueindex][root.axis])<result_set.worstDist():
            kdtree_knn_search(root.left,db,result_set,query)
    

    

    return False

def main():
    # configuration
    leaf_size = 10
    k = 8

    db_np = origindata
    construction_time_sum = 0
    begin_t = time.time()
    root = kdtree_construction(db_np, leaf_size=leaf_size)
    construction_time_sum += time.time() - begin_t
    
    # depth = [0]
    # max_depth = [0]
    # traverse_kdtree(root, depth, max_depth)
    # print("tree max depth: %d" % max_depth[0])

    
    
    
    knn_time_sum = 0
    for ind ,p in enumerate(origindata):
        begin_t = time.time()
        result_set = KNNResultSet(capacity=k)
        kdtree_knn_search(root, db_np, result_set, p)
        knn_time_sum += time.time() - begin_t
        
    print('kdtreebuilding:',construction_time_sum)
    print('kdtree:',knn_time_sum)
    


if __name__ == '__main__':
    main()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值