0 前言
?? 優(yōu)質(zhì)競(jìng)賽項(xiàng)目系列,今天要分享的是
?? **基于深度學(xué)習(xí)得交通車輛流量分析 **
該項(xiàng)目較為新穎,適合作為競(jìng)賽課題方向,學(xué)長(zhǎng)非常推薦!
??學(xué)長(zhǎng)這里給一個(gè)題目綜合評(píng)分(每項(xiàng)滿分5分)
- 難度系數(shù):3分
- 工作量:3分
- 創(chuàng)新點(diǎn):5分
?? 更多資料, 項(xiàng)目分享:
https://gitee.com/dancheng-senior/postgraduate文章來源地址http://www.zghlxwxcb.cn/news/detail-733813.html
1 課題背景
在智能交通系統(tǒng)中,利用監(jiān)控視頻進(jìn)行車流量統(tǒng)計(jì)是一個(gè)研究熱點(diǎn)。交管部門通過實(shí)時(shí)、準(zhǔn)確地采集車流量信息,可以合理分配交通資源、提高道路通行效率,有效預(yù)防和應(yīng)對(duì)城市交通擁堵問題。同時(shí)隨著車輛數(shù)量的增加,交通違章現(xiàn)象頻出,例如渣土車違規(guī)上道、工程車輛違規(guī)進(jìn)入城市主干道、車輛停放在消防通道等,這一系列的交通違規(guī)行為給城市安全埋下了巨大隱患。對(duì)于交通管理者而言,加強(qiáng)對(duì)特定車輛的識(shí)別和分類管理尤為重要。然而,在實(shí)際監(jiān)控識(shí)別車輛時(shí),相當(dāng)一部分車輛圖像存在圖像不全或者遮擋問題,極大降低了監(jiān)控識(shí)別準(zhǔn)確率。如何準(zhǔn)確識(shí)別車輛,是當(dāng)前車輛檢測(cè)的重點(diǎn)。
根據(jù)實(shí)際情況,本文將車輛分為家用小轎車、貨車兩類進(jìn)行車輛追蹤和速度識(shí)別。
2 實(shí)現(xiàn)效果
可識(shí)別圖片視頻中的轎車和貨車數(shù)量,檢測(cè)行駛速度并實(shí)時(shí)顯示。
關(guān)鍵代碼
?
# 目標(biāo)檢測(cè)
def yolo_detect(self, im):
img = self.preprocess(im)
pred = self.m(img, augment=False)[0]
pred = pred.float()
pred = non_max_suppression(pred, self.conf_thres, self.iou_thres )
pred_boxes = []
for det in pred:
if det is not None and len(det):
det[:, :4] = scale_coords(
img.shape[2:], det[:, :4], im.shape).round()
for *x, conf, cls_id in det:
lbl = self.names[int(cls_id)]
x1, y1 = int(x[0]), int(x[1])
x2, y2 = int(x[2]), int(x[3])
pred_boxes.append(
(x1, y1, x2, y2, lbl, conf))
return pred_boxes
3 DeepSORT車輛跟蹤
多目標(biāo)在線跟蹤算法 SORT(simple online andrealtime
tracking)利用卡爾曼濾波和匈牙利匹配,將跟蹤結(jié)果和檢測(cè)結(jié)果之間的IoU作為代價(jià)矩陣,實(shí)現(xiàn)了一種簡(jiǎn)單高效并且實(shí)用的跟蹤范式。但是 SORT
算法的缺陷在于所使用的關(guān)聯(lián)度量(association
metric)只有在狀態(tài)估計(jì)不確定性較低的情況下有效,因此算法執(zhí)行時(shí)會(huì)出現(xiàn)大量身份切換現(xiàn)象,當(dāng)目標(biāo)被遮擋時(shí)跟蹤失敗。為了改善這個(gè)問題,Deep SORT
將目標(biāo)的運(yùn)動(dòng)信息和外觀信息相結(jié)合作為關(guān)聯(lián)度量,改善目標(biāo)消失后重新出現(xiàn)導(dǎo)致的跟蹤失敗問題。
3.1 Deep SORT多目標(biāo)跟蹤算法
跟蹤處理和狀態(tài)估計(jì)
Deep SORT
利用檢測(cè)器的結(jié)果初始化跟蹤器,每個(gè)跟蹤器都會(huì)設(shè)置一個(gè)計(jì)數(shù)器,在卡爾曼濾波之后計(jì)數(shù)器累加,當(dāng)預(yù)測(cè)結(jié)果和檢測(cè)結(jié)果成功匹配時(shí),該計(jì)數(shù)器置為0。在一段時(shí)間內(nèi)跟蹤器沒有匹配到合適的檢測(cè)結(jié)果,則刪除該跟蹤器。Deep
SORT 為每一幀中新出現(xiàn)的檢測(cè)結(jié)果分配跟蹤器,當(dāng)該跟蹤器連續(xù)3幀的預(yù)測(cè)結(jié)果都能匹配檢測(cè)結(jié)果,則確認(rèn)是出現(xiàn)了新的軌跡,否則刪除該跟蹤器。
Deep SORT使用 8維狀態(tài)空間描述目標(biāo)的狀態(tài)和在圖像坐標(biāo)系中的運(yùn)動(dòng)信息。
表示目標(biāo)檢測(cè)框的中心坐標(biāo)
分別表示檢測(cè)框的寬高比例和高度,
表示前面四個(gè)參數(shù)在圖像坐標(biāo)中的相對(duì)速度。算法使用具有恒定速度模型和線性觀測(cè)模型的標(biāo)準(zhǔn)卡爾曼濾波器,將檢測(cè)框參數(shù)
作為對(duì)象狀態(tài)的直接觀測(cè)值。
分配問題
Deep SORT
結(jié)合運(yùn)動(dòng)信息和外觀信息,使用匈牙利算法匹配預(yù)測(cè)框和跟蹤框。對(duì)于運(yùn)動(dòng)信息,算法使用馬氏距離描述卡爾曼濾波預(yù)測(cè)結(jié)果和檢測(cè)器結(jié)果的關(guān)聯(lián)程度,如公式中:
分別表示第 j 個(gè)檢測(cè)結(jié)果和第 i
個(gè)預(yù)測(cè)結(jié)果的狀態(tài)向量,Si 表示檢測(cè)結(jié)果和平均跟蹤結(jié)
當(dāng)目標(biāo)運(yùn)動(dòng)信息不確定性較低的時(shí)候,馬氏距離是一種合適的關(guān)聯(lián)因子,但是當(dāng)目標(biāo)遮擋或者鏡頭視角抖動(dòng)時(shí),僅使用馬氏距離關(guān)聯(lián)會(huì)導(dǎo)致目標(biāo)身份切換。因此考慮加入外觀信息,對(duì)每一個(gè)檢測(cè)框
dj 計(jì)算出對(duì)應(yīng)的外觀特征描述符 rj ,并且設(shè)置。對(duì)于每一個(gè)跟蹤軌跡 k
設(shè)置特征倉庫,用來保存最近100條目標(biāo)成功關(guān)聯(lián)的特征描述符,
。計(jì)算第 i 個(gè)跟蹤框和第 j
個(gè)檢測(cè)框最小余弦距離,如公式:
當(dāng)小于指定的閾值,認(rèn)為關(guān)聯(lián)成功。
馬氏距離在短時(shí)預(yù)測(cè)情況下可以提供可靠的目標(biāo)位置信息,使用外觀特征的余弦相似度可以在目標(biāo)遮擋又重新出現(xiàn)時(shí)恢復(fù)目標(biāo)
ID,為了使兩種度量的優(yōu)勢(shì)互補(bǔ),使用線性加權(quán)的方式進(jìn)行結(jié)合:
3.2 算法流程
Deepsort算法的工作流程如下圖所示:
源碼流程
主函數(shù)部分整體邏輯是比較簡(jiǎn)單的,首先是將命令行參數(shù)進(jìn)行解析,解析的內(nèi)容包括,MOTChanlleng序列文件所在路徑、需要檢測(cè)文件所在的目錄等一系列參數(shù)。解析之后傳遞給run方法,開始運(yùn)行。
進(jìn)入run函數(shù)之后,首先會(huì)收集流信息,包括圖片名稱,檢測(cè)結(jié)果以及置信度等,后續(xù)會(huì)將這些流信息傳入到檢測(cè)框生成函數(shù)中,生成檢測(cè)框列表。然后會(huì)初始化metric對(duì)象,metric對(duì)象簡(jiǎn)單來說就是度量方式,在這個(gè)地方我們可以選擇兩種相似度的度量方式,第一種叫做余弦相似度度量,另一種叫做歐拉相似度度量。通過metric對(duì)象我們來初始化追蹤器。
接著根據(jù)display參數(shù)開始生成對(duì)應(yīng)的visuializer,如果選擇將檢測(cè)結(jié)果進(jìn)行可視化展示,那么便會(huì)生成Visualization對(duì)象,我從這個(gè)類中可以看到,它主要是調(diào)用opencv
image
viewer來講追蹤的結(jié)果進(jìn)行展示。如果display是false則會(huì)生成一個(gè)NoVisualization對(duì)象,它一個(gè)虛擬可視化對(duì)象,它以給定的順序循環(huán)遍歷所有幀以更新跟蹤器,而無需執(zhí)行任何可視化。兩者主要區(qū)別其實(shí)就是是否調(diào)用opencv將圖片展示出來。其實(shí)前邊我們所做的一系列工作可以說都是準(zhǔn)備的工作,實(shí)際上核心部分就是在執(zhí)行這個(gè)run方法之后。此處我們可以看到,在run方法中傳入了一個(gè)frame_callback函數(shù),這個(gè)frame_callback函數(shù)可以說是整個(gè)算法的核心部分,每一幀的圖片都會(huì)執(zhí)行該函數(shù)。
4 YOLOV5算法
6月9日,Ultralytics公司開源了YOLOv5,離上一次YOLOv4發(fā)布不到50天。而且這一次的YOLOv5是完全基于PyTorch實(shí)現(xiàn)的!在我們還對(duì)YOLOv4的各種高端操作、豐富的實(shí)驗(yàn)對(duì)比驚嘆不已時(shí),YOLOv5又帶來了更強(qiáng)實(shí)時(shí)目標(biāo)檢測(cè)技術(shù)。按照官方給出的數(shù)目,現(xiàn)版本的YOLOv5每個(gè)圖像的推理時(shí)間最快0.007秒,即每秒140幀(FPS),但YOLOv5的權(quán)重文件大小只有YOLOv4的1/9。
目標(biāo)檢測(cè)架構(gòu)分為兩種,一種是two-stage,一種是one-stage,區(qū)別就在于 two-stage 有region
proposal過程,類似于一種海選過程,網(wǎng)絡(luò)會(huì)根據(jù)候選區(qū)域生成位置和類別,而one-stage直接從圖片生成位置和類別。今天提到的 YOLO就是一種
one-stage方法。YOLO是You Only Look Once的縮寫,意思是神經(jīng)網(wǎng)絡(luò)只需要看一次圖片,就能輸出結(jié)果。YOLO
一共發(fā)布了五個(gè)版本,其中 YOLOv1 奠定了整個(gè)系列的基礎(chǔ),后面的系列就是在第一版基礎(chǔ)上的改進(jìn),為的是提升性能。
YOLOv5有4個(gè)版本性能如圖所示:
4.1 網(wǎng)絡(luò)架構(gòu)圖
YOLOv5是一種單階段目標(biāo)檢測(cè)算法,該算法在YOLOv4的基礎(chǔ)上添加了一些新的改進(jìn)思路,使其速度與精度都得到了極大的性能提升。主要的改進(jìn)思路如下所示:
4.2 輸入端
在模型訓(xùn)練階段,提出了一些改進(jìn)思路,主要包括Mosaic數(shù)據(jù)增強(qiáng)、自適應(yīng)錨框計(jì)算、自適應(yīng)圖片縮放;
- Mosaic數(shù)據(jù)增強(qiáng):Mosaic數(shù)據(jù)增強(qiáng)的作者也是來自YOLOv5團(tuán)隊(duì)的成員,通過隨機(jī)縮放、隨機(jī)裁剪、隨機(jī)排布的方式進(jìn)行拼接,對(duì)小目標(biāo)的檢測(cè)效果很不錯(cuò)
4.3 基準(zhǔn)網(wǎng)絡(luò)
融合其它檢測(cè)算法中的一些新思路,主要包括:Focus結(jié)構(gòu)與CSP結(jié)構(gòu);
4.4 Neck網(wǎng)絡(luò)
在目標(biāo)檢測(cè)領(lǐng)域,為了更好的提取融合特征,通常在Backbone和輸出層,會(huì)插入一些層,這個(gè)部分稱為Neck。Yolov5中添加了FPN+PAN結(jié)構(gòu),相當(dāng)于目標(biāo)檢測(cè)網(wǎng)絡(luò)的頸部,也是非常關(guān)鍵的。
FPN+PAN的結(jié)構(gòu)
這樣結(jié)合操作,F(xiàn)PN層自頂向下傳達(dá)強(qiáng)語義特征(High-Level特征),而特征金字塔則自底向上傳達(dá)強(qiáng)定位特征(Low-
Level特征),兩兩聯(lián)手,從不同的主干層對(duì)不同的檢測(cè)層進(jìn)行特征聚合。
FPN+PAN借鑒的是18年CVPR的PANet,當(dāng)時(shí)主要應(yīng)用于圖像分割領(lǐng)域,但Alexey將其拆分應(yīng)用到Y(jié)olov4中,進(jìn)一步提高特征提取的能力。
4.5 Head輸出層
輸出層的錨框機(jī)制與YOLOv4相同,主要改進(jìn)的是訓(xùn)練時(shí)的損失函數(shù)GIOU_Loss,以及預(yù)測(cè)框篩選的DIOU_nms。
對(duì)于Head部分,可以看到三個(gè)紫色箭頭處的特征圖是40×40、20×20、10×10。以及最后Prediction中用于預(yù)測(cè)的3個(gè)特征圖:
?
①==>40×40×255
②==>20×20×255
③==>10×10×255
?
相關(guān)代碼
?
class Detect(nn.Module):
stride = None # strides computed during build
onnx_dynamic = False # ONNX export parameter
def __init__(self, nc=80, anchors=(), ch=(), inplace=True): # detection layer
super().__init__()
self.nc = nc # number of classes
self.no = nc + 5 # number of outputs per anchor
self.nl = len(anchors) # number of detection layers
self.na = len(anchors[0]) // 2 # number of anchors
self.grid = [torch.zeros(1)] * self.nl # init grid
self.anchor_grid = [torch.zeros(1)] * self.nl # init anchor grid
self.register_buffer('anchors', torch.tensor(anchors).float().view(self.nl, -1, 2)) # shape(nl,na,2)
self.m = nn.ModuleList(nn.Conv2d(x, self.no * self.na, 1) for x in ch) # output conv
self.inplace = inplace # use in-place ops (e.g. slice assignment)
def forward(self, x):
z = [] # inference output
for i in range(self.nl):
x[i] = self.m[i](x[i]) # conv
bs, _, ny, nx = x[i].shape # x(bs,255,20,20) to x(bs,3,20,20,85)
x[i] = x[i].view(bs, self.na, self.no, ny, nx).permute(0, 1, 3, 4, 2).contiguous()
if not self.training: # inference
if self.onnx_dynamic or self.grid[i].shape[2:4] != x[i].shape[2:4]:
self.grid[i], self.anchor_grid[i] = self._make_grid(nx, ny, i)
y = x[i].sigmoid()
if self.inplace:
y[..., 0:2] = (y[..., 0:2] * 2 - 0.5 + self.grid[i]) * self.stride[i] # xy
y[..., 2:4] = (y[..., 2:4] * 2) ** 2 * self.anchor_grid[i] # wh
else: # for YOLOv5 on AWS Inferentia https://github.com/ultralytics/yolov5/pull/2953
xy = (y[..., 0:2] * 2 - 0.5 + self.grid[i]) * self.stride[i] # xy
wh = (y[..., 2:4] * 2) ** 2 * self.anchor_grid[i] # wh
y = torch.cat((xy, wh, y[..., 4:]), -1)
z.append(y.view(bs, -1, self.no))
return x if self.training else (torch.cat(z, 1), x)
def _make_grid(self, nx=20, ny=20, i=0):
d = self.anchors[i].device
if check_version(torch.__version__, '1.10.0'): # torch>=1.10.0 meshgrid workaround for torch>=0.7 compatibility
yv, xv = torch.meshgrid([torch.arange(ny).to(d), torch.arange(nx).to(d)], indexing='ij')
else:
yv, xv = torch.meshgrid([torch.arange(ny).to(d), torch.arange(nx).to(d)])
grid = torch.stack((xv, yv), 2).expand((1, self.na, ny, nx, 2)).float()
anchor_grid = (self.anchors[i].clone() * self.stride[i]) \
.view((1, self.na, 1, 1, 2)).expand((1, self.na, ny, nx, 2)).float()
return grid, anchor_grid
5 最后
?? 更多資料, 項(xiàng)目分享:文章來源:http://www.zghlxwxcb.cn/news/detail-733813.html
https://gitee.com/dancheng-senior/postgraduate
到了這里,關(guān)于競(jìng)賽 深度學(xué)習(xí)交通車輛流量分析 - 目標(biāo)檢測(cè)與跟蹤 - python opencv的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!