【精选】如何将自定义点云转换成semanticKitti格式

【精选】如何将自定义点云转换成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)

猜你喜欢

转载自blog.csdn.net/weixin_42628609/article/details/134339866