解决的问题
两个几何形状相同的点云文件,其一有颜色信息,其二有标签信息,桌面点云处理软件中通常不能将其融合。
一个存在的困难是两个文件中,尽管形状相同,但各点的顺序可能存在差异。因此,组合乱序点云中的特征不能直接将数据融合,还需先将对应点进行匹配排序。
例子
pc1:
x | y | z | r | g | b |
---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 |
2 | 3 | 4 | 5 | 6 | 7 |
3 | 4 | 5 | 6 | 7 | 8 |
pc2:
x | y | z | label |
---|---|---|---|
2 | 3 | 4 | 12 |
1 | 2 | 3 | 11 |
4 | 5 | 6 | 14 |
3 | 4 | 5 | 13 |
out:
x | y | z | r | g | b | label |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 11 |
2 | 3 | 4 | 5 | 6 | 7 | 12 |
3 | 4 | 5 | 6 | 7 | 8 | 13 |
代码
注:代码中的ply
库见 Python ply pcd 点云读取源码
match_feature.py
"""
整合2个点云中的特征, 将pc2中的额外特征添加到pc1中
原理是为pc1中的每个点找到pc2中最近的点, 将不同名特征添加给pc1
2个点云中的点可以是乱序的
Sample: python match_feature.py pc1.ply pc2.ply *out.ply
github.com/DLW3D
2023/11/27
"""
import time
import sys
import numpy as np
from ply import read_ply, write_ply
from pykdtree.kdtree import KDTree
t = time.time()
# 获取命令行参数
if len(sys.argv) <= 2:
print('Error no input file')
print('Sample: python match_feature.py pc1.ply pc2.ply *out.ply')
sys.exit(2)
elif len(sys.argv) == 3:
pc1_path = sys.argv[1]
pc2_path = sys.argv[2]
if pc1_path.endswith('.ply'):
out_path = pc1_path.replace('.ply', '_matched.ply')
else:
raise Exception('Error pc1 format: %s' % pc1_path)
else:
pc1_path = sys.argv[1]
pc2_path = sys.argv[2]
out_path = sys.argv[3]
# 从参考数据中提取对应点
print('正在加载数据...')
pc1_data = read_ply(pc1_path)
pc2_data = read_ply(pc2_path)
# 匹配点
print('正在匹配点...')
kdtree = KDTree(np.stack([pc2_data['x'], pc2_data['y'], pc2_data['z']], axis=1))
distances, indices = kdtree.query(np.stack([pc1_data['x'], pc1_data['y'], pc1_data['z']], axis=1), k=1)
if distances.max() > 1e-6:
print('Warning: max distance diff: %s' % distances.max())
# 自动识别缺失特征
pc1_names = list(pc1_data.dtype.names)
pc2_names = list(pc2_data.dtype.names)
diff_names = [name for name in pc2_names if name not in pc1_names]
print('额外特征:%s' % diff_names)
# 保存数据
print('正在保存数据...')
write_ply(out_path,
[pc1_data[n] for n in pc1_names] + [pc2_data[n][indices] for n in diff_names],
pc1_names + diff_names)
print('Saved in %s' % out_path)
print('运行耗时: %sm %ss' % (int((time.time() - t) / 60), int((time.time() - t) % 60)))