动手学无人驾驶(4):基于激光雷达点云数据3D目标检测
上一篇文章《動手學無人駕駛(3):基于激光雷達3D多目標追蹤》介紹了3D多目標追蹤,多目標追蹤里使用的傳感器數據為激光雷達Lidar檢測到的數據,本文就介紹如何基于激光雷達點云數據進行3D目標檢測。
論文地址:《PointRCNN: 3D Object Proposal Generation and Detection from Point Cloud》
Github項目地址:https://github.com/sshaoshuai/PointRCNN
KITTI數據集百度云下載地址:百度網盤 請輸入提取碼 提取碼: ct4q
在介紹論文前,大家可以先看看論文作者此前分享的3D目標檢測報告,報告里對論文有詳細介紹,B站地址為:基于點云場景的三維物體檢測算法及應用_嗶哩嗶哩_bilibili
基于點云場景的三維物體檢測算法及應用
目錄
1.KITTI數據集
2.PointRCNN
3.目標檢測結果
參考資料
1.KITTI數據集
KITTI目標檢測數據集(http://www.cvlibs.net/datasets/kitti/eval_object.php?obj_benchmark=3d)中共包含7481張訓練圖片和7518張測試圖片,以及與之相對應的點云數據,label文件以及calib文件。這里以訓練集編號為000008的場景為例介紹KITTI數據集中calib和label文件所含信息。
?????????????????????????????????????????????????????????????????? (training/image_2/000008.png)
Calib文件:在KITTI數據采集過程中使用到了攝像頭和激光雷達,采集到的數據坐標分別為攝像頭坐標和激光雷達坐標下的測量值,這時就需要calib文件將攝像頭與激光雷達進行標定,calib文件通過txt格式來保存。
velodyne:velodyne中存儲著激光雷達點云采集到數據,數據以2進制格式存儲(.bin),點云數據存儲格式為(N,4)。N為激光線束反射點個數,4代表著:(x,y,z,r),分別返回在3個坐標軸上的位置和反射率。
label文件:label文件為標注數據,以txt格式保存,000008.txt中標注的object內容如下:
Car 0.88 3 -0.69 0.00 192.37 402.31 374.00 1.60 1.57 3.23 -2.70 1.74 3.68 -1.29
Car 0.00 1 2.04 334.85 178.94 624.50 372.04 1.57 1.50 3.68 -1.17 1.65 7.86 1.90
Car 0.34 3 -1.84 937.29 197.39 1241.00 374.00 1.39 1.44 3.08 3.81 1.64 6.15 -1.31
Car 0.00 1 -1.33 597.59 176.18 720.90 261.14 1.47 1.60 3.66 1.07 1.55 14.44 -1.25
Car 0.00 0 1.74 741.18 168.83 792.25 208.43 1.70 1.63 4.08 7.24 1.55 33.20 1.95
Car 0.00 0 -1.65 884.52 178.31 956.41 240.18 1.59 1.59 2.47 8.48 1.75 19 -1.25
DontCare -1 -1 -10 800.38 163.67 825.45 184.07 -1 -1 -1 -1000 -1000 -1000 -10
DontCare -1 -1 -10 859.58 172.34 886.26 194.51 -1 -1 -1 -1000 -1000 -1000 -10
DontCare -1 -1 -10 801.81 163.96 825.20 183.59 -1 -1 -1 -1000 -1000 -1000 -10
DontCare -1 -1 -10 826.87 162.28 845.84 178.86 -1 -1 -1 -1000 -1000 -1000 -10
關于label文件的理解可以參考下面的代碼:
class Object3d(object):''' 3d object label '''def __init__(self, label_file_line):data = label_file_line.split(' ')data[1:] = [float(x) for x in data[1:]]# extract label, truncation, occlusionself.type = data[0] # 'Car', 'Pedestrian', ...self.truncation = data[1] # truncated pixel ratio [0..1]self.occlusion = int(data[2]) # 0=visible, 1=partly occluded, 2=fully occluded, 3=unknownself.alpha = data[3] # object observation angle [-pi..pi]# extract 2d bounding box in 0-based coordinatesself.xmin = data[4] # leftself.ymin = data[5] # topself.xmax = data[6] # rightself.ymax = data[7] # bottomself.box2d = np.array([self.xmin,self.ymin,self.xmax,self.ymax])# extract 3d bounding box informationself.h = data[8] # box heightself.w = data[9] # box widthself.l = data[10] # box length (in meters)self.t = (data[11],data[12],data[13]) # location (x,y,z) in camera coord.self.ry = data[14] # yaw angle (around Y-axis in camera coordinates) [-pi..pi]def print_object(self):print('Type, truncation, occlusion, alpha: %s, %d, %d, %f' % \(self.type, self.truncation, self.occlusion, self.alpha))print('2d bbox (x0,y0,x1,y1): %f, %f, %f, %f' % \(self.xmin, self.ymin, self.xmax, self.ymax))print('3d bbox h,w,l: %f, %f, %f' % \(self.h, self.w, self.l))print('3d bbox location, ry: (%f, %f, %f), %f' % \(self.t[0],self.t[1],self.t[2],self.ry))2.PointRCNN
3D目標檢測模型PointRCNN借鑒了PointNet++和RCNN的思想,提出了自底向上的生成和調整候選檢測區域的算法,網絡結構如下圖所示:
PointRCNN的網絡結構分為兩個階段:第一階段自底向上生成3D候選預測框;第二階段在規范坐標中對候選預測框進行搜索和微調,得到更為精確的預測框作為檢測結果。
第一階段:對3D點云數據進行語義分割和前背景劃分,生成候選預測框,有如下三個關鍵步驟:
-
點云特征提取:通過PointNet++對點云數據進行編碼和解碼,提取點云特征向量。
-
前景點分割:根據提取的點云特征向量,使用focal loss區分前景點和背景點。focal loss能有效地平衡前景點和背景點比例失衡問題,從而得到更為準確的分類效果。
-
生成候選框:采用候選框箱模型(bin)的方法,將前背景點分割信息生成預測候選框。
舉例來說,將候選框定義為參數(x,y,z,h,w,l,θ)表征的空間中的箱體,其中(x,y,z)為箱體中心坐標,( h,w,l)為箱體在中心坐標方向上的大小,θ為鳥瞰視角上(y方向從上往下看)箱體在x-z平面的角度。
bin的執行方式為:先根據前景點的分割信息粗分其所屬的箱體;再在箱體內部對其做回歸,得到箱體參數作為預測框;最后對預測框做NMS(Non-Max Suppress,非極大值抑制),得到最終預測候選框。
第二階段:在規范坐標中微調候選預測框,獲得最終的檢測結果,有如下四個關鍵部分:
-
區域池化:對候選框內每個點的特征進行池化。
-
坐標轉化:為了更好地獲取局部信息,需要將多個候選區域中的前景點坐標(同一個坐標系)轉化為局域坐標系中的規范坐標(以預測框為中心點的多個坐標系),如下圖所示:
-
特征編碼:將規范坐標時丟失的深度信息、規范后的坐標信息、前后背景語義信息等經過多層感知機提取特征,作為每個點的編碼特征。
-
微調預測框:經過上一步編碼后的特征,經PointNet++網絡進行特征提取,最后回歸得到局部坐標系下的3D預測框。
PointRCNN網絡代碼如下:
class PointRCNN(nn.Module):def __init__(self, num_classes, use_xyz=True, mode='TRAIN'):super().__init__()assert cfg.RPN.ENABLED or cfg.RCNN.ENABLEDif cfg.RPN.ENABLED:self.rpn = RPN(use_xyz=use_xyz, mode=mode)if cfg.RCNN.ENABLED:rcnn_input_channels = 128 # channels of rpn featuresif cfg.RCNN.BACKBONE == 'pointnet':self.rcnn_net = RCNNNet(num_classes=num_classes, input_channels=rcnn_input_channels, use_xyz=use_xyz)elif cfg.RCNN.BACKBONE == 'pointsift':pass else:raise NotImplementedErrordef forward(self, input_data):if cfg.RPN.ENABLED:output = {}# rpn inferencewith torch.set_grad_enabled((not cfg.RPN.FIXED) and self.training):if cfg.RPN.FIXED:self.rpn.eval()rpn_output = self.rpn(input_data)output.update(rpn_output)# rcnn inferenceif cfg.RCNN.ENABLED:with torch.no_grad():rpn_cls, rpn_reg = rpn_output['rpn_cls'], rpn_output['rpn_reg']backbone_xyz, backbone_features = rpn_output['backbone_xyz'], rpn_output['backbone_features']rpn_scores_raw = rpn_cls[:, :, 0]rpn_scores_norm = torch.sigmoid(rpn_scores_raw)seg_mask = (rpn_scores_norm > cfg.RPN.SCORE_THRESH).float()pts_depth = torch.norm(backbone_xyz, p=2, dim=2)# proposal layerrois, roi_scores_raw = self.rpn.proposal_layer(rpn_scores_raw, rpn_reg, backbone_xyz) # (B, M, 7)output['rois'] = roisoutput['roi_scores_raw'] = roi_scores_rawoutput['seg_result'] = seg_maskrcnn_input_info = {'rpn_xyz': backbone_xyz,'rpn_features': backbone_features.permute((0, 2, 1)),'seg_mask': seg_mask,'roi_boxes3d': rois,'pts_depth': pts_depth}if self.training:rcnn_input_info['gt_boxes3d'] = input_data['gt_boxes3d']rcnn_output = self.rcnn_net(rcnn_input_info)output.update(rcnn_output)elif cfg.RCNN.ENABLED:output = self.rcnn_net(input_data)else:raise NotImplementedErrorreturn output3.目標檢測結果
介紹完KITTI數據集和PointRCNN模型后,現在用作者預訓練好的模型進行目標預測。
1)首先是準備數據,從百度網盤里下載KITTI數據后按如下方式排放數據:
PointRCNN ├── data │ ├── KITTI │ │ ├── ImageSets │ │ ├── object │ │ │ ├──training │ │ │ ├──calib & velodyne & label_2 & image_2 │ │ │ ├──testing │ │ │ ├──calib & velodyne & image_2 ├── lib ├── pointnet2_lib ├── tools2)將預訓練好的模型放入/tools文件夾下,執行如下命令,此時會對驗證集進行預測:
python eval_rcnn.py --cfg_file cfgs/default.yaml --ckpt PointRCNN.pth --batch_size 1 --eval_mode rcnn --set RPN.LOC_XZ_FINE False預測結果如下,這里預測的平均準確率略小于原作者預測的。
# 原作者預測結果 Car AP@0.70, 0.70, 0.70: bbox AP:96.91, 89.53, 88.74 bev AP:90.21, 87.89, 85.51 3d AP:89.19, 78.85, 77.91 aos AP:96.90, 89.41, 88.54 # 使用預訓練模型輸出的結果 Car AP@0.70, 0.70, 0.70: bbox AP:90.3697, 78.9661, 76.0439 bev AP:88.8698, 75.5572, 69.7311 3d AP:83.3413, 66.9504, 60.1443 aos AP:90.30, 78.51, 75.45參考資料
飛槳火力全開,重磅上線3D模型:PointNet++、PointRCNN!
PointRCNN地址:https://github.com/sshaoshuai/PointRCNN
論文:《PointRCNN: 3D Object Proposal Generation and Detection from Point Cloud》
總結
以上是生活随笔為你收集整理的动手学无人驾驶(4):基于激光雷达点云数据3D目标检测的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ltmoh.exe是什么进程 有什么用
- 下一篇: 从零实现一个3D目标检测算法(3):Po