PaddleX助力无人驾驶:基于YOLOv3的车辆检测和车道线分割实战
【飛槳開發(fā)者說】梁瑛平,北京理工大學(xué)徐特立學(xué)院本科二年級(jí),人工智能開發(fā)愛好者。
項(xiàng)目簡(jiǎn)介
無人駕駛汽車?yán)脗鞲衅骷夹g(shù)、信號(hào)處理技術(shù)、通訊技術(shù)和計(jì)算機(jī)技術(shù)等,通過集成視覺、激光雷達(dá)、超聲傳感器、微波雷達(dá)、GPS、里程計(jì)、磁羅盤等多種車載傳感器來辨識(shí)汽車所處的環(huán)境和狀態(tài),并根據(jù)所獲得的道路信息、交通信號(hào)的信息、車輛位置和障礙物信息做出分析和判斷,向主控計(jì)算機(jī)發(fā)出期望控制,控制車輛轉(zhuǎn)向和速度,從而實(shí)現(xiàn)無人駕駛車輛依據(jù)自身意圖和環(huán)境的擬人駕駛。
該項(xiàng)目使用PaddleX提供的YOLOv3模型,在 UA-DETRAC 車輛檢測(cè)數(shù)據(jù)集進(jìn)行訓(xùn)練;
訓(xùn)練結(jié)果能夠檢測(cè)到car,van,bus等不同類型車輛,mAP為0.73;
并使用開源車道檢測(cè)算法,實(shí)現(xiàn)了無人駕駛部分的視覺感知——車輛檢測(cè)和車道線分割;
最終效果
PaddleX工具簡(jiǎn)介
PaddleX是飛槳全流程開發(fā)工具,集飛槳核心框架、模型庫、工具及組件等深度學(xué)習(xí)開發(fā)所需全部能力于一身,打通深度學(xué)習(xí)開發(fā)全流程,并提供簡(jiǎn)明易懂的Python API,方便用戶根據(jù)實(shí)際生產(chǎn)需求進(jìn)行直接調(diào)用或二次開發(fā),為開發(fā)者提供飛槳全流程開發(fā)的最佳實(shí)踐。目前,該工具代碼已開源于GitHub,同時(shí)可訪問PaddleX在線使用文檔,快速查閱使用教程和API文檔說明。
PaddleX代碼GitHub鏈接:
https://github.com/PaddlePaddle/PaddleX
PaddleX文檔鏈接:
https://paddlex.readthedocs.io/zh_CN/latest/index.html
PaddleX官網(wǎng)鏈接:
https://www.paddlepaddle.org.cn/paddle/paddlex
項(xiàng)目過程回放
一、準(zhǔn)備PaddleX環(huán)境
1. 安裝PaddleX庫
pip?install?paddlex?-i?https://mirror.baidu.com/pypi/simple2. 設(shè)置工作路徑,并使用0號(hào)GPU卡
import?matplotlib matplotlib.use('Agg')?import?os os.environ['CUDA_VISIBLE_DEVICES']?=?'0' import?paddlex?as?pdxos.chdir('/home/aistudio/work/')二、準(zhǔn)備數(shù)據(jù)
1. 數(shù)據(jù)集簡(jiǎn)介
數(shù)據(jù)集使用 UA-DETRAC 數(shù)據(jù)集,是一個(gè)具有挑戰(zhàn)性的真實(shí)多目標(biāo)檢測(cè)和多目標(biāo)跟蹤基準(zhǔn)。該數(shù)據(jù)集由10小時(shí)的視頻組成,這些視頻由中國(guó)北京和天津的24個(gè)不同地點(diǎn)使用Cannon EOS 550D攝像機(jī)拍攝。視頻以每秒 25 幀 (fps) 的速度錄制,分辨率為 960×540 像素。UA-DETRAC 數(shù)據(jù)集中有超過 140 000 個(gè)幀,手動(dòng)標(biāo)注了 8250 輛車,總共有 121 萬個(gè)標(biāo)記了邊界框的目標(biāo)。
2. 準(zhǔn)備所需文件
PaddleX同時(shí)支持VOC和COCO兩種格式的數(shù)據(jù),需要的文件有:
labels.txt:保存目標(biāo)類別的文件,不包括背景類;
train_list.txt和val_list.txt:保存訓(xùn)練/測(cè)試所需的圖片和標(biāo)注文件的相對(duì)路徑;
三、數(shù)據(jù)預(yù)處理
1. 設(shè)置圖像數(shù)據(jù)預(yù)處理和數(shù)據(jù)增強(qiáng)模塊
具體參數(shù)見:
https://paddlex.readthedocs.io/zh_CN/latest/apis/transforms/det_transforms.html
from?paddlex.det?import?transforms train_transforms?=?transforms.Compose([transforms.MixupImage(mixup_epoch=250),transforms.RandomDistort(),transforms.RandomExpand(),transforms.RandomCrop(),transforms.Resize(target_size=608,?interp='RANDOM'),transforms.RandomHorizontalFlip(),transforms.Normalize(), ])eval_transforms?=?transforms.Compose([transforms.Resize(target_size=608,?interp='CUBIC'),transforms.Normalize(), ])2. 定義數(shù)據(jù)迭代器
訓(xùn)練集總共有6000張圖片,我們選取5800訓(xùn)練,剩余200張進(jìn)行測(cè)試。
base?=?'./VOC2012/'train_dataset?=?pdx.datasets.VOCDetection(data_dir=base,file_list=base+'train_list.txt',label_list=base+'labels.txt',transforms=train_transforms,shuffle=True) eval_dataset?=?pdx.datasets.VOCDetection(data_dir=base,file_list=base+'val_list.txt',label_list=base+'labels.txt',transforms=eval_transforms)2020-05-11?07:57:15?[INFO]????Starting?to?read?file?list?from?dataset...2020-05-11?07:57:16?[INFO]????5800?samples?in?file?./VOC2012/train_list.txt creating?index...index?created!2020-05-11?07:57:17?[INFO]????Starting?to?read?file?list?from?dataset...2020-05-11?07:57:17?[INFO]????200?samples?in?file?./VOC2012/val_list.txt creating?index...index?created!參數(shù)說明:
data_dir (str): 數(shù)據(jù)集所在的目錄路徑。
file_list (str): 描述數(shù)據(jù)集圖片文件和對(duì)應(yīng)標(biāo)注文件的文件路徑(文本內(nèi)每行路徑為相對(duì)data_dir的相對(duì)路徑)。
label_list (str): 描述數(shù)據(jù)集包含的類別信息文件路徑。
transforms (paddlex.det.transforms): 數(shù)據(jù)集中每個(gè)樣本的預(yù)處理/增強(qiáng)算子,詳見paddlex.det.transforms。
num_workers (int|str):數(shù)據(jù)集中樣本在預(yù)處理過程中的線程或進(jìn)程數(shù)。默認(rèn)為’auto’。當(dāng)設(shè)為’auto’時(shí),根據(jù)系統(tǒng)的實(shí)際CPU核數(shù)設(shè)置num_workers: 如果CPU核數(shù)的一半大于8,則num_workers為8,否則為CPU核數(shù)的一半。
buffer_size (int): 數(shù)據(jù)集中樣本在預(yù)處理過程中隊(duì)列的緩存長(zhǎng)度,以樣本數(shù)為單位。默認(rèn)為100。
parallel_method (str): 數(shù)據(jù)集中樣本在預(yù)處理過程中并行處理的方式,支持’thread’線程和’process’進(jìn)程兩種方式。默認(rèn)為’thread’(Windows和Mac下會(huì)強(qiáng)制使用thread,該參數(shù)無效)。
shuffle (bool): 是否需要對(duì)數(shù)據(jù)集中樣本打亂順序。默認(rèn)為False。
四、定義YOLOv3模型并開始訓(xùn)練
1. YOLOv3簡(jiǎn)介:
論文地址:
https://arxiv.org/abs/1804.02767
‘Sometimes you just kinda phone it in for a year, you know?’
作者說他一年大部分時(shí)間去刷 Twitter 了,然后玩了(play around)一陣子 GAN,正好剩下一點(diǎn)時(shí)間,就改進(jìn)了一下 YOLO 算法,提出了 YOLO v3。YOLOv3添加了ResNet中提出的殘差結(jié)果和FPN中提出的通過上采樣得到的特征金字塔結(jié)果。它最顯著特征是它可以三種不同的比例進(jìn)行檢測(cè),最終輸出是通過在特征圖上應(yīng)用1 x 1內(nèi)核生成的。在YOLO v3中,通過在網(wǎng)絡(luò)中三個(gè)不同位置的三個(gè)不同大小的特征圖上使用1 x 1大小的卷積來完成檢測(cè)。
num_classes?=?len(train_dataset.labels) print('class?num:',?num_classes) model?=?pdx.det.YOLOv3(num_classes=num_classes,?backbone='DarkNet53') model.train(num_epochs=4,train_dataset=train_dataset,train_batch_size=4,eval_dataset=eval_dataset,learning_rate=0.000125,lr_decay_epochs=[400,?800],save_interval_epochs=2,log_interval_steps=200,save_dir='./yolov3_darknet53',use_vdl=True) class?num:?42020-05-11?08:15:15?[INFO]????Load?pretrain?weights?from?./yolov3_darknet53/pretrain/DarkNet53.2020-05-11?08:15:16?[INFO]????There?are?260?varaibles?in?./yolov3_darknet53/pretrain/DarkNet53?are?loaded.參數(shù)說明:
num_classes (int): 類別數(shù)。默認(rèn)為80。
backbone (str): YOLOv3的backbone網(wǎng)絡(luò),取值范圍為[‘DarkNet53’, ‘ResNet34’, ‘MobileNetV1’, ‘MobileNetV3_large’]。默認(rèn)為’MobileNetV1’。
anchors (list|tuple): anchor框的寬度和高度,為None時(shí)表示使用默認(rèn)值 [[10, 13], [16, 30], [33, 23], [30, 61], [62, 45], [59, 119], [116, 90], [156, 198], [373, 326]]。
anchor_masks (list|tuple): 在計(jì)算YOLOv3損失時(shí),使用anchor的mask索引,為None時(shí)表示使用默認(rèn)值 [[6, 7, 8], [3, 4, 5], [0, 1, 2]]。
ignore_threshold (float): 在計(jì)算YOLOv3損失時(shí),IoU大于ignore_threshold的預(yù)測(cè)框的置信度被忽略。默認(rèn)為0.7。
nms_score_threshold (float): 檢測(cè)框的置信度得分閾值,置信度得分低于閾值的框應(yīng)該被忽略。默認(rèn)為0.01。
nms_topk (int): 進(jìn)行NMS時(shí),根據(jù)置信度保留的最大檢測(cè)框數(shù)。默認(rèn)為1000。
nms_keep_topk (int): 進(jìn)行NMS后,每個(gè)圖像要保留的總檢測(cè)框數(shù)。默認(rèn)為100。
nms_iou_threshold (float): 進(jìn)行NMS時(shí),用于剔除檢測(cè)框IOU的閾值。默認(rèn)為0.45。
label_smooth (bool): 是否使用label smooth。默認(rèn)值為False。
train_random_shapes (list|tuple): 訓(xùn)練時(shí)從列表中隨機(jī)選擇圖像大小。默認(rèn)值為[320, 352, 384, 416, 448, 480, 512, 544, 576, 608]。
五、評(píng)估模型
使用 evaluate 方法進(jìn)行模型評(píng)估,最終mAP為0.73左右。
六、加載模型用于測(cè)試
image_name?=?'./test6.jpg'
result?=?model.predict(image_name)
pdx.det.visualize(image_name,?result,?threshold=0.5,?save_dir='./output/')
檢測(cè)結(jié)果:
七、定義車道線檢測(cè)模型
這里使用了開源的項(xiàng)目:
https://github.com/Sharpiless/advanced_lane_detection
該車道檢測(cè)算法流程為:
(1)給定一組棋盤圖像(在camera_cal文件夾內(nèi)),計(jì)算相機(jī)校準(zhǔn)矩陣和失真系數(shù)。
(2)根據(jù)校準(zhǔn)矩陣和失真系數(shù)對(duì)原始圖像應(yīng)用失真校正。
(3)使用顏色變換,漸變等創(chuàng)建閾值二進(jìn)制圖像。
(4)應(yīng)用透視變換以校正二進(jìn)制圖像(“鳥瞰”)。
(5)檢測(cè)圖像中車道像素并擬合,以找到車道邊界。
(6)將檢測(cè)到的車道邊界矯正到原始圖像。
具體實(shí)現(xiàn)如下:
import?numpy?as?np import?cv2,?pickle,?glob,?os import?matplotlib.pyplot?as?plt import?matplotlib.image?as?mpimg import?toolsfrom?moviepy.editor?import?VideoFileClip from?IPython.display?import?HTML #?code?adopted?from:?https://github.com/t-lanigan/vehicle-detection-and-tracking/blob/master/road_sensor.py class?GlobalObjects:def?__init__(self):self.__set_folders()self.__set_hyper_parameters()self.__set_perspective()self.__set_kernels()self.__set_mask_regions()def?__set_folders(self):#?Use?one?slash?for?paths.self.camera_cal_folder?=?'camera_cal/'self.test_images?=?glob.glob('test_images/*.jpg')self.output_image_path?=?'output_images/test_'self.output_movie_path?=?'output_movies/done_'def?__set_hyper_parameters(self):self.img_size???=?(1280,?720)?#?(x,y)?values?for?img?size?(cv2?uses?this)self.img_shape??=?(self.img_size[1],?self.img_size[0])?#?(y,x)?As?numpy?spits?outreturndef?__set_kernels(self):"""Kernels?used?for?image?processing"""self.clahe?=?cv2.createCLAHE(clipLimit=2.0,?tileGridSize=(8,8))def?__set_perspective(self):"""The?src?points?draw?a?persepective?trapezoid,?the?dst?points?drawthem?as?a?square.??M?transforms?x,y?from?trapezoid?to?square?fora?birds-eye?view.??M_inv?does?the?inverse."""src?=?np.float32([[(.42?*?self.img_shape[1],.65?*?self.img_shape[0]?),(.58?*?self.img_shape[1],?.65?*?self.img_shape[0]),(0?*?self.img_shape[1],self.img_shape[0]),(1?*?self.img_shape[1],?self.img_shape[0])]])dst?=?np.float32([[0,0],[self.img_shape[1],0],[0,self.img_shape[0]],[self.img_shape[1],self.img_shape[0]]])self.M?=?cv2.getPerspectiveTransform(src,?dst)self.M_inv?=?cv2.getPerspectiveTransform(dst,?src)def?__set_mask_regions(self):"""These?are?verticies?used?for?clipping?the?image."""self.bottom_clip?=?np.int32(np.int32([[[60,0],?[1179,0],?[1179,650],?[60,650]]]))self.roi_clip?=??np.int32(np.int32([[[640,?425],?[1179,550],?[979,719],[299,719],?[100,?550],?[640,?425]]])) class?LaneFinder(object):"""The?mighty?LaneFinder?takes?in?a?video?from?the?front?camera?of?a?self?driving?carand?produces?a?new?video?with?the?traffic?lanes?highlighted?and?statistics?about?wherethe?car?is?relative?to?the?center?of?the?lane?shown."""????def?__init__(self):self.g?????????????=?GlobalObjects()????????self.thresholder???=?tools.ImageThresholder()self.distCorrector?=?tools.DistortionCorrector(self.g.camera_cal_folder)self.histFitter????=?tools.HistogramLineFitter()self.laneDrawer????=?tools.LaneDrawer()self.leftLane??????=?tools.Line()self.rightLane?????=?tools.Line()returndef?__image_pipeline(self,?img):"""The?pipeline?for?processing?images.?Globals?g?are?added?to?functions?that?needaccess?to?global?variables."""resized?????=?self.__resize_image(img)undistorted?=?self.__correct_distortion(resized)warped??????=?self.__warp_image_to_biv(undistorted)thresholded?=?self.__threshold_image(warped)lines???????=?self.__get_lane_lines(thresholded)result??????=?self.__draw_lane_lines(undistorted,?thresholded,?include_stats=False)return?resultdef?__draw_lane_lines(self,?undistorted,?thresholded,?include_stats):lines?=?{'left_line':?self.leftLane,'right_line':?self.rightLane?}return?self.laneDrawer.draw_lanes(undistorted,thresholded,lines,self.g.M_inv,include_stats)def?__get_lane_lines(self,?img):self.leftLane????=?self.histFitter.get_line(img,?self.leftLane,?'left')self.rightLane???=?self.histFitter.get_line(img,?self.rightLane,?'right')return?Truedef?__mask_region(self,?img,?vertices):"""Masks?a?region?specified?by?clockwise?vertices."""mask?=?np.zeros_like(img)???if?len(img.shape)?>?2:channel_count?=?img.shape[2]??#?i.e.?3?or?4?depending?on?your?imageignore_mask_color?=?(255,)?*?channel_countelse:ignore_mask_color?=?255cv2.fillConvexPoly(mask,?vertices,?ignore_mask_color)masked_image?=?cv2.bitwise_and(img,?mask)return?masked_image?def?__resize_image(self,?img):"""Image?is?resized?to?the?selected?size?for?the?project."""return?cv2.resize(img,?self.g.img_size,?interpolation?=?cv2.INTER_CUBIC)def?__correct_distortion(self,?img):return?self.distCorrector.undistort(img)def?__threshold_image(self,?img):return?self.thresholder.get_thresholded_image(img)def?__warp_image_to_biv(self,?img):return?cv2.warpPerspective(img,?self.g.M,?self.g.img_size)def?test_one_image(self,?pt):image?=?(mpimg.imread(pt))return?self.__image_pipeline(image)八、最終效果
%matplotlib?inline obj?=?LaneFinder() result?=?obj.test_one_image('./output/visualize_test6.jpg')print(type(result),?result.shape)plt.figure(figsize=(15,12)) plt.imshow(result) plt.savefig('result.png') plt.show()小結(jié)
本項(xiàng)目使用PaddleX提供的高層接口,快速、高效地完成了無人駕駛?cè)蝿?wù)中車輛檢測(cè)部分的模型訓(xùn)練和部署。最大的感受就是Paddle為開發(fā)者提供了很好的開發(fā)環(huán)境。通過Python API方式完成全流程使用或集成,該模型提供全面、靈活、開放的深度學(xué)習(xí)功能,有更高的定制化空間以及更低門檻的方式快速完成產(chǎn)業(yè)模型部署,并提供了應(yīng)用層的軟件和可視化服務(wù)。
數(shù)據(jù)集選擇和模型選擇。訓(xùn)練集最終選擇了UA-DETRAC 數(shù)據(jù)集,并且我也將該訓(xùn)練集轉(zhuǎn)換到了VOC格式并在AI Studio上公開。模型最終選擇了PaddleX提供的YOLOv3,該算法不僅在COCO、VOC等公開數(shù)據(jù)集上表現(xiàn)出色,并且實(shí)踐證明在別的任務(wù)中,YOLOv3也具有比其他算法更好的泛化能力。
開發(fā)過程:開發(fā)最初效果并不理想,在UA-DETRAC數(shù)據(jù)集上的mAP僅有0.64左右。這里嘗試了調(diào)整學(xué)習(xí)率、批次大小等超參數(shù),并使用了不同的數(shù)據(jù)增強(qiáng)方法,但是提升效果微乎其微。最終查閱原論文發(fā)現(xiàn),YOLOv3使用了K-means的方法獲取預(yù)選框大小。修改并訓(xùn)練后,檢測(cè)精度得到了很好的提升(mAP為0.79左右)。
人工設(shè)置anchor大小的弊端:
修改前anchor使用默認(rèn)值。這些anchor雖然能夠提供不同尺寸和長(zhǎng)寬比的ROI,但是針對(duì)特定任務(wù),有一些大小的anchor并不能很好地表征目標(biāo),甚至?xí)~外增加不必要的計(jì)算量。比如針對(duì)小目標(biāo)檢測(cè),較大的anchor幾乎不會(huì)被選取為正樣本。而且如果anchor的尺寸和目標(biāo)的尺寸差異較大,則會(huì)影響模型的檢測(cè)效果。
YOLO的作者Joseph Redmon等建議使用K-means聚類來代替人工設(shè)計(jì),通過對(duì)訓(xùn)練集的真值框進(jìn)行聚類,自動(dòng)生成一組更加適合數(shù)據(jù)集的anchor大小,可以使網(wǎng)絡(luò)的檢測(cè)效果更好。
K-means算法獲取anchor大小:
Joseph Redmon希望anchor能夠滿足與目標(biāo)框盡可能相似并且距離盡可能相近,所以他提出了選取anchor大小的度量d:
其中IOU表示真值框和預(yù)選框的交并比。
因此,最終算法步驟為:
隨機(jī)選取K個(gè)box作為初始anchor;
使用IOU度量,將每個(gè)box分配給與其距離最近的anchor;
計(jì)算每個(gè)簇中所有box寬和高的均值,更新anchor;
重復(fù)2、3步,直到anchor不再變化,或者達(dá)到了最大迭代次數(shù)。
在UA-DETRAC數(shù)據(jù)集上得到的anchor大小為:
(13,11),(17,15),(23,17),(29,23),
(41,29),(68,33),(51,46),(93,57),(135,95)
相關(guān)代碼參考:
https://github.com/ybcc2015/DeepLearning-Utils/tree/master/Anchor-Kmeans
def?iou(boxes,?anchors):#?計(jì)算IOUw_min?=?np.minimum(boxes[:,?0,?np.newaxis],?anchors[np.newaxis,?:,?0])h_min?=?np.minimum(boxes[:,?1,?np.newaxis],?anchors[np.newaxis,?:,?1])inter?=?w_min?*?h_minbox_area?=?boxes[:,?0]?*?boxes[:,?1]anchor_area?=?anchors[:,?0]?*?anchors[:,?1]union?=?box_area[:,?np.newaxis]?+?anchor_area[np.newaxis]return?inter?/?(union?-?inter)def?fit(self,?boxes):if?self.n_iter?>?0:self.n_iter?=?0np.random.seed(self.random_seed)n?=?boxes.shape[0]#?初始化隨機(jī)anchor大小self.anchors_?=?boxes[np.random.choice(n,?self.k,?replace=True)]self.labels_?=?np.zeros((n,))while?True:self.n_iter?+=?1if?self.n_iter?>?self.max_iter:breakself.ious_?=?self.iou(boxes,?self.anchors_)distances?=?1?-?self.ious_cur_labels?=?np.argmin(distances,?axis=1)#?如果anchor大小不再變化,則表示已收斂,終止迭代if?(cur_labels?==?self.labels_).all():break#?更新anchor大小for?i?in?range(self.k):self.anchors_[i]?=?np.mean(boxes[cur_labels?==?i],?axis=0)self.labels_?=?cur_labels此案例應(yīng)用的目標(biāo)檢測(cè)場(chǎng)景,還可以通過飛槳目標(biāo)檢測(cè)套件PaddleDetection來實(shí)現(xiàn),這里提供了更專業(yè)的端到端開發(fā)套件和工具,歡迎感興趣的小伙伴動(dòng)手實(shí)踐一把。
PaddleDetection GitHub項(xiàng)目地址:
https://github.com/PaddlePaddle/PaddleDetection
更多資源
如在使用過程中有問題,可加入飛槳官方QQ群進(jìn)行交流:703252161。
飛槳PaddleX技術(shù)交流QQ群:1045148026
如果您想詳細(xì)了解更多飛槳的相關(guān)內(nèi)容,請(qǐng)參閱以下文檔。
官網(wǎng)地址:
https://www.paddlepaddle.org.cn
更多PaddleX的應(yīng)用方法,歡迎訪問項(xiàng)目地址:
GitHub:?
https://github.com/PaddlePaddle/PaddleX
Gitee:?
https://gitee.com/paddlepaddle/PaddleX
飛槳開源框架項(xiàng)目地址:
GitHub:
https://github.com/PaddlePaddle/Paddle
Gitee:?
https://gitee.com/paddlepaddle/Paddle
END
總結(jié)
以上是生活随笔為你收集整理的PaddleX助力无人驾驶:基于YOLOv3的车辆检测和车道线分割实战的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Install MicroSoft Dy
- 下一篇: Magic Powder - 2 Cod