【精选】如何将自定义点云转换成semanticKitti格式
文章目录
前言
在训练基于SemanticKitti数据集的点云语义分割模型时,如(rangeNet++、CENet、UniSeg等),若需要使用自定义数据集进行训练,则要将自定义的pcd点云转换成和SemanticKitti一样的数据格式。SemanticKitti的点云由二进制的“.bin”文件存储,其对应的标签由二进制的“.label”文件存储。
1、使用SSE对pcd点云进行标注
- 这里推荐使用SSE(semantic segmentation editor)标注工具对点云进行标注。SSE安装方法和标注教程本文不讲述,如果小伙伴们有需要,后续我们也可以出一个关于SSE的教程。
SSE传送门: https://github.com/Hitachi-Automotive-And-Industry-Lab/semantic-segmentation-editor/releases
这里假设我们已经有了自定义数据的ascii格式的pcd点云文件,其格式如下(注意:SSE只支持对ascii格式的pcd点云文件的标注,任何其它格式的点云文件都不支持!)
- 使用SSE标注完之后,导出ascii格式的pcd文件。如下所示,细心的小伙伴已经发现,标注后的点云丢失了intensity信息,而semanticKitti数据集的点云是带有intensity信息的。因此,我们用读取没标注之前的pcd文件用于生成二进制的点云bin文件,读取标注之后的pcd文件用于生成二进制的label文件。其中的“label”代表这个点的标签序号,“object”代表这个点的实例序号,如果是语义分割,则所有点的“object”都是“-1”。
2、读取原始pcd文件并生成二进制bin文件
- 读取单个原始pcd文件的每个点的xyz及intensity信息,并返回到numpy数组。
def read_ori_pcd(input_path):
lidar = []
with open(input_path, 'r') as f:
line = f.readline().strip()
while line:
linestr = line.split(" ")
if len(linestr) == 4:
linestr_convert = list(map(float, linestr))
lidar.append(linestr_convert)
line = f.readline().strip()
return np.array(lidar)
- 对输入的目录里的所有原始pcd文件批量转换成二进制bin文件。如下所示,需要先对intensity进行归一化后才能存储,才符合semanticKitti的格式。
def convert2bin(input_pcd_dir, output_bin_dir):
file_list = os.listdir(input_pcd_dir)
if not os.path.exists(output_bin_dir):
os.makedirs(output_bin_dir)
for file in file_list:
(filename, extension) = os.path.splitext(file)
velodyne_file = os.path.join(input_pcd_dir, filename) + '.pcd'
p_xyzi = read_ori_pcd(velodyne_file)
p_xyzi = p_xyzi.reshape((-1, 4)).astype(np.float32)
min_val = np.amin(p_xyzi[:, 3])
max_val = np.amax(p_xyzi[:, 3])
p_xyzi[:, 3] = (p_xyzi[:, 3] - min_val)/(max_val-min_val)
p_xyzi[:, 3] = np.round(p_xyzi[:, 3], decimals=2)
p_xyzi[:, 3] = np.minimum(p_xyzi[:, 3], 0.99)
velodyne_file_new = os.path.join(output_bin_dir, filename) + '.bin'
p_xyzi.tofile(velodyne_file_new)
3、读取SSE标注后的pcd文件并生成二进制label文件
- 读取单个标注后的pcd文件的每个点的xyz及label和object信息,并返回到numpy数组。
def read_labeled_pcd(filepath):
lidar = []
with open(filepath, 'r') as f:
line = f.readline().strip()
while line:
linestr = line.split(" ")
if len(linestr) == 5:
linestr_convert = list(map(float, linestr))
lidar.append(linestr_convert)
line = f.readline().strip()
return np.array(lidar)
- 对输入的目录里的所有标注后pcd文件批量转换成二进制label文件。如下所示,只需要读取每个点的“label”值并保存二进制格式即可。这里每个点的“label”其实是一一对应了上文的bin点云文件里的每个点。
def convert2label(input_pcd_dir, output_label_dir):
file_list = os.listdir(input_pcd_dir)
if not os.path.exists(output_label_dir):
os.makedirs(output_label_dir)
for file in file_list:
(filename, extension) = os.path.splitext(file)
velodyne_file = os.path.join(input_pcd_dir, filename) + '.pcd'
p_xyz_label_object = read_labeled_pcd(velodyne_file)
p_xyz_label_object = p_xyz_label_object.reshape((-1, 5))
label = p_xyz_label_object[:, 3].astype(np.int32)
label = label.reshape(-1)
velodyne_file_new = os.path.join(output_label_dir, filename) + '.label'
label.tofile(velodyne_file_new)
4、完整代码
import os
import numpy as np
def read_ori_pcd(input_path):
lidar = []
with open(input_path, 'r') as f:
line = f.readline().strip()
while line:
linestr = line.split(" ")
if len(linestr) == 4:
linestr_convert = list(map(float, linestr))
lidar.append(linestr_convert)
line = f.readline().strip()
return np.array(lidar)
def read_labeled_pcd(filepath):
lidar = []
with open(filepath, 'r') as f:
line = f.readline().strip()
while line:
linestr = line.split(" ")
if len(linestr) == 5:
linestr_convert = list(map(float, linestr))
lidar.append(linestr_convert)
line = f.readline().strip()
return np.array(lidar)
def convert2bin(input_pcd_dir, output_bin_dir):
file_list = os.listdir(input_pcd_dir)
if not os.path.exists(output_bin_dir):
os.makedirs(output_bin_dir)
for file in file_list:
(filename, extension) = os.path.splitext(file)
velodyne_file = os.path.join(input_pcd_dir, filename) + '.pcd'
p_xyzi = read_ori_pcd(velodyne_file)
p_xyzi = p_xyzi.reshape((-1, 4)).astype(np.float32)
min_val = np.amin(p_xyzi[:, 3])
max_val = np.amax(p_xyzi[:, 3])
p_xyzi[:, 3] = (p_xyzi[:, 3] - min_val)/(max_val-min_val)
p_xyzi[:, 3] = np.round(p_xyzi[:, 3], decimals=2)
p_xyzi[:, 3] = np.minimum(p_xyzi[:, 3], 0.99)
velodyne_file_new = os.path.join(output_bin_dir, filename) + '.bin'
p_xyzi.tofile(velodyne_file_new)
def convert2label(input_pcd_dir, output_label_dir):
file_list = os.listdir(input_pcd_dir)
if not os.path.exists(output_label_dir):
os.makedirs(output_label_dir)
for file in file_list:
(filename, extension) = os.path.splitext(file)
velodyne_file = os.path.join(input_pcd_dir, filename) + '.pcd'
p_xyz_label_object = read_labeled_pcd(velodyne_file)
p_xyz_label_object = p_xyz_label_object.reshape((-1, 5))
label = p_xyz_label_object[:, 3].astype(np.int32)
label = label.reshape(-1)
velodyne_file_new = os.path.join(output_label_dir, filename) + '.label'
label.tofile(velodyne_file_new)
if __name__ == "__main__":
ori_pcd_dir = "这里输入所有原始点云pcd文件的存放目录"
ori_bin_dir = "这里输入生成后的点云bin文件的保存路径"
convert2bin(ori_pcd_dir, ori_bin_dir)
labeled_pcd_dir = "这里输入所有标注之后的点云pcd文件的存放目录"
labeled_bin_dir = "这里输入生成后的点云label文件的保存路径"
convert2label(labeled_pcd_dir, labeled_bin_dir)