这篇文章介绍了pcd文件的数据结构及原理,我介绍了读取pcd bin file的简易方法以及使用open3d直接读取的简易方法(真的非常简便),如果你不想看原理直接拉到最后就好 :)
目录
pcd文件数据结构
首先我们来搞懂使用pcl生成的pcd文件的数据结构,我参考了这篇文章,在这里介绍下重点:The PCD (Point Cloud Data) file format — Point Cloud Library 1.14.1-dev documentation
Example of the header fields: seperateded using new lines /n
# .PCD v0.7 - Point Cloud Data file format\n
VERSION 0.7\n
FIELDS x y z intensity\n
SIZE 4 4 4 4\n
TYPE F F F F\n
COUNT 1 1 1 1\n
WIDTH 2048\n
HEIGHT 128\n
VIEWPOINT 0 0 0 1 0 0 0\n
POINTS 262144\n
DATA binary\n
\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\....
pcd文件包含headers和binary数据:
headers包含整个数据集的主要信息,本数据集一共有262144个点,宽度是2048是lidar旋转一圈,一行有2048个点,height是128,代表每列有128个点,这些点组成了点云矩阵。
每个点包含四个信息 x y z intensity, 每个信息的数据类型是float,也就是包含4个bytes,一共4×8=32个bits。所以每4×4个bytes是一个点的全部信息,全部的信息长度是262144×16=4194304再加上前面的headers的长度。
如何读取pcd bin 文件(原理版)
我拥有的原始数据是同事从pcl生成的bin file,首先读取headers信息,然后把按照bytes读取的点赋值到相应的list当中,参考了很多代码,source列在这里:
https://github.com/nutonomy/nuscenes-devkit/blob/master/python-sdk/nuscenes/utils/data_classes.py
Reading Point Cloud .bin file using C++ - Stack Overflow
Conversion of binary lidar data (.bin) to point cloud data (.pcd) format - Stack Overflow
#source: https://github.com/nutonomy/nuscenes-devkit/blob/master/python-sdk/nuscenes/utils/data_classes.py
#source: https://stackoverflow.com/questions/65570879/reading-point-cloud-bin-file-using-c
#source: https://stackoverflow.com/questions/60506331/conversion-of-binary-lidar-data-bin-to-point-cloud-data-pcd-format
def read_pcd_bin(binFilePath):
"""
source: https://pointclouds.org/documentation/tutorials/pcd_file_format.html
PCD file have headers:
VERSION
FIELDS
SIZE
TYPE
COUNT
WIDTH
HEIGHT
VIEWPOINT
POINTS
DATA
Example of the header fields: seperateded using new lines /n
# .PCD v0.7 - Point Cloud Data file format\n
VERSION 0.7\n
FIELDS x y z intensity\n
SIZE 4 4 4 4\n
TYPE F F F F\n
COUNT 1 1 1 1\n
WIDTH 2048\n
HEIGHT 128\n
VIEWPOINT 0 0 0 1 0 0 0\n
POINTS 262144\n
DATA binary\n
\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
"""
headers = []
with open(binFilePath, mode = 'rb') as f:
for line in f:
line = line.strip().decode('utf-8')
headers.append(line)
if line.startswith('DATA'):
break
data_binary = f.read()
# Get the headers and check if they appeared as expected
assert headers[0].startswith('#')
assert headers[1].startswith('VERSION')
sizes = headers[3].split(' ')[1:] # first element is the name of header
types = headers[4].split(' ')[1:]
type = ''.join(types)
counts = headers[5].split(' ')[1:]
width = int(headers[6].split(' ')[1])
height = int(headers[7].split(' ')[1])
points = int(headers[9].split(' ')[1])
data_format = headers[10].split(' ')[1]
feature_nb = len(types) #4
assert width > 0
assert height > 0
assert data_format == 'binary'
sizes_int = [int(size) for size in sizes]
sizes_sum = sum(sizes_int)
list_pcd = []
list_intensity = []
# Decode each point
for i in range (points):
start = i * sizes_sum
end = start +sizes_sum
point = data_binary[start:end]
x,y,z,intensity = struct.unpack(type.lower(), point)
list_pcd.append([x,y,z])
list_intensity.append(intensity)
pcd_np = np.asarray(list_pcd)
intensity_np = np.asarray(list_intensity)
# Create Open3D point cloud
pcd = o3d.geometry.PointCloud()
pcd.points = o3d.utility.Vector3dVector(pcd_np)
# Create a color map based on intensity
intensity_normalized = (intensity_np - np.min(intensity_np)) / (np.max(intensity_np) - np.min(intensity_np))
colors = o3d.utility.Vector3dVector(np.stack([intensity_normalized, np.zeros_like(intensity_normalized), np.zeros_like(intensity_normalized)], axis=1))
pcd.colors = colors
return pcd
# Example usage
file_name = './0000000120.bin'
pcd = read_pcd_bin(file_name)
o3d.visualization.draw_geometries([pcd])
一步到位简易读取 pcd bin file
我花了一上午再加上昨天2h,还和我同事一起研究pcd的结构,好不容易搞明白,但是我突然找到了一个open3d关于如何读取pcd文件的帖子,原来只需要把bin file的文件后缀改成。pcd,然后使用open3d的集成方法read_point_cloud就解决了这个问题,一整个大无语
Can't read pcd.bin format · Issue #680 · isl-org/Open3D · GitHub
filename = './0000000120.pcd'
pcd = o3d.io.read_point_cloud(filename, format='auto', remove_nan_points=False, remove_infinite_points=False, print_progress=False)
o3d.visualization.draw_geometries([pcd])
这个浪费的时间能生成一篇文章,顿时感觉碎得没有那么难过了