AVOD:点云数据与BEV图的处理及可视化
文章目錄
- 前言
- 1. 點云數據可視化
- 2. 點云數據校準
- 3. 轉為BEV圖
- 結束語
前言
??本篇主要記錄對AVOD代碼的學習與理解,主要是KITTI數據集中3D Object Detection任務中的點云數據和BEV圖的處理,為方面理解其中的操作,博主在這里加入了可視化的操作。
??本篇博客使用的樣本編號為000274,RGB圖像如下:
1. 點云數據可視化
??點云數據保存在velodyne文件夾內,數據文件的格式是.bin,保存了x, y, z三軸坐標以及反射值r信息,數據格式為float32,通過numpy可以讀取文件,具體如下:
import numpy as npif __name__ == '__main__':bin_file = r'F:\DataSet\Kitti\object\velodyne\000274.bin'pointcloud = np.fromfile(bin_file, dtype=np.float32, count=-1).reshape([-1, 4])print('pointcloud shape: ', pointcloud.shape)# pointcloud shape: (120438, 4)??為了更加直觀的看點云圖像,這里不再使用matplotlib,而是更專業的三維可視化工具包mayavi,具體操作如下:
import numpy as np from mayavi import mlabif __name__ == '__main__':bin_file = r'F:\DataSet\Kitti\object\velodyne\000274.bin'pointcloud = np.fromfile(bin_file, dtype=np.float32, count=-1).reshape([-1, 4])x = pointcloud[:, 0] # x position of pointy = pointcloud[:, 1] # y position of pointz = pointcloud[:, 2] # z position of pointr = pointcloud[:, 3] # reflectance value of pointd = np.sqrt(x ** 2 + y ** 2) # Map Distance from sensorvals = 'height'if vals == "height":col = zelse:col = dfig = mlab.figure(bgcolor=(1, 1, 1), size=(700, 500))mlab.points3d(x, y, z,d, # Values used for Colormode="point",colormap='spectral', # 'bone', 'copper', 'gnuplot', 'spectral', 'summer'# color=(0, 1, 0), # Used a fixed (r,g,b) insteadfigure=fig)mlab.show()??可視化結果如下:
??調整一下視角與RGB圖保持一致:
2. 點云數據校準
??RGB圖片使用的是左側第二個彩色攝像機,即image_2,因此需要將雷達數據進行坐標變化,將其映射到攝像機坐標系中,計算公式為:
y = P2 * R0_rect * Tr_velo_to_cam * x??大致計算流程:
# Read calibration info frame_calib = calib_utils.read_calibration(calib_dir, img_idx) x, y, z, i = calib_utils.read_lidar(velo_dir=velo_dir, img_idx=img_idx)# Calculate the point cloud pts = np.vstack((x, y, z)).T pts = calib_utils.lidar_to_cam_frame(pts, frame_calib)# Only keep points in front of camera (positive z) pts = pts[pts[:, 2] > 0] point_cloud = pts.T# Project to image frame point_in_im = calib_utils.project_to_image(point_cloud, p=frame_calib.p2).T??具體實現:
# 為了方便可視化數據,這里封裝了對點云進行可視化的函數 def visu_point_cloud(x, y, z):d = np.sqrt(x ** 2 + y ** 2) # Map Distance from sensorvals = 'distance'if vals == "distance":col = delse:col = zfig = mlab.figure(bgcolor=(1, 1, 1), size=(700, 500))mlab.points3d(x, y, z,col, # Values used for Colormode="point",colormap='spectral', # 'bone', 'copper', 'gnuplot', 'spectral', 'summer'# color=(0, 1, 0), # Used a fixed (r,g,b) insteadfigure=fig)mlab.show()def get_lidar_point_cloud(calib_dir, velo_dir, img_idx, im_size=None, min_intensity=None):""" Calculates the lidar point cloud, and optionally returns only thepoints that are projected to the image.:param calib_dir: directory with calibration files:param velo_dir: directory with velodyne files:param img_idx: image index:param im_size: (optional) 2 x 1 list containing the size of the imageto filter the point cloud [w, h]:param min_intensity: (optional) minimum intensity required to keep a point:return: (3, N) point_cloud in the form [[x,...][y,...][z,...]]"""# Read calibration infoframe_calib = read_calibration(calib_dir, img_idx)x, y, z, i = read_lidar(velo_dir=velo_dir, img_idx=img_idx)# Calculate the point cloudpts = np.vstack((x, y, z)).Tpts = lidar_to_cam_frame(pts, frame_calib)# The given image is assumed to be a 2D imageif not im_size:point_cloud = pts.Treturn point_cloudelse:# Only keep points in front of camera (positive z)pts = pts[pts[:, 2] > 0]point_cloud = pts.T# Project to image framepoint_in_im = project_to_image(point_cloud, p=frame_calib['p2']).T# Filter based on the given image sizeimage_filter = (point_in_im[:, 0] > 0) & \(point_in_im[:, 0] < im_size[0]) & \(point_in_im[:, 1] > 0) & \(point_in_im[:, 1] < im_size[1])if not min_intensity:return pts[image_filter].Telse:intensity_filter = i > min_intensitypoint_filter = np.logical_and(image_filter, intensity_filter)return pts[point_filter].Timg_path = r'F:\DataSet\Kitti\object\image_2' lidar_path = r'F:\DataSet\Kitti\object\velodyne' calib_path = r'F:\DataSet\Kitti\object\calib' planes_path = r'F:\DataSet\Kitti\object\planes' label_path = r'F:\DataSet\Kitti\object\label_2' img_idx = 274if __name__ == '__main__':point_cloud = get_lidar_point_cloud(calib_path, lidar_path, img_idx, im_size)visu_point_cloud(point_cloud[0], point_cloud[1], point_cloud[2])??可視化結果如下:
??調整視角為俯視圖:
3. 轉為BEV圖
??BEV,即bird's eye view,鳥瞰圖
??鳥瞰圖的計算需要用到地面數據,即空間上的點投影到某個平面,需要知道該平面的平面方程,平面方程的表達式為:ax+by+cz+d=0ax+by+cz+d=0ax+by+cz+d=0??空間上的點P(x0,y0,z0)P(x_0,y_0,z_0)P(x0?,y0?,z0?)到平面上的距離為:distance=∣ax0+by0+cz0+d∣a2+b2+c2distance=\frac {|ax_0+by_0+cz_0+d|} {\sqrt {a^2+b^2+c^2}}distance=a2+b2+c2?∣ax0?+by0?+cz0?+d∣???讀取KITTI數據集中的地面數據:
# 000274.txt # Plane Width 4 Height 1 -2.143976e-03 -9.997554e-01 2.201096e-02 1.707479e+00 # 分別表示a, b, c, d四個參數值??點云數據轉BEV時高度分辨率為0.5,根據點云數據可以得到x,y和z軸上的數據,即x_col,y_col,z_col,然后使用np.lexsort()對x軸進行排序:
sorted_order = np.lexsort((y_col, z_col, x_col)) # 對 x_col 進行排序 # 如果 x_col 中的數值一樣,則比較 z_col 中相應索引下的值的大小 # 如果還相同,再比較 y_col 中的元素??將 y_col 中的元素置為0,即只保留x和z軸上的數據,然后使用np.view()對數值類型進行變換:
# 定義12字節的數據類型 dt = np.dtype((np.void, 12))# 先使用np.ascontiguousarray將一個內存不連續存儲的數組轉換為內存連續存儲的數組,使得運行速度更快 # 再使用np.view按指定方式對內存區域進行切割,來完成數據類型的轉換 # discrete_pts(n, 3) --> contiguous_array(n, 1) # itemsize輸出array元素的字節數 contiguous_array = np.ascontiguousarray(discrete_pts).view(dtype=dt)??離散點云數據discrete_pts的shape為(n,3),其數值類型為np.int32,4字節,總字節為n34Byte,現將其轉為12Byte的數據,即保持總字節數不變:n112,轉換完成后的shape為(n,1)。
??對上述的數據進行去重:
# 去除數組中的重復數字,并進行排序 _, unique_indices = np.unique(contiguous_array, return_index=True) unique_indices.sort() # 得到不重復的數據 # voxel 體素(三維) # pixel 像素(二維) voxel_coords = discrete_pts[unique_indices] # 將索引值映射到原點 voxel_coords -= -400??計算每個體素中數據點的數量,即后一個索引值減去當前索引值:
num_points_in_voxel = np.diff(unique_indices) num_points_in_voxel = np.append(num_points_in_voxel,discrete_pts_2d.shape[0] -unique_indices[-1])??計算每個體素中數據點的高度,通過計算點到平面方程的距離:
# voxel (x, y, z) # 平面方程: ax + by + cz + d = 0 height_in_voxel = (a*x + b*y + c*z + d) / np.sqrt(a**2 + b**2 + c**2)??對高度信息進行縮放,height_in_voxel = height_in_voxel / 0.5,根據二維索引值voxel_coords(去除y軸)與高度信息即可得到BEV數據,密度信息的計算:
# N is num points in voxel # x = 16 density_map = min(1.0, log(N+1)/log(x))??最終的BEV是由5個高度信息和1個密度信息組成,其shape為(700, 800, 6)。
??三維可視化結果如下:
??RGB可視化如下:
結束語
??后續繼續更新^_^
總結
以上是生活随笔為你收集整理的AVOD:点云数据与BEV图的处理及可视化的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 互联网+创新创业大赛项目计划书,个人原创
- 下一篇: 微信小程序:动画效果集合