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上完成并运行
catalogue
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()