智慧交通day02-车流量检测实现12:基于yoloV3的目标检测
在本章節(jié)代碼編寫中,發(fā)現(xiàn)之前的代碼所處的環(huán)境是python3,因此導(dǎo)致了cv2.dnn.readNetFromDarknet()在代碼運(yùn)行中導(dǎo)致了i[0]的獲值失敗,故總結(jié)如下:
cv2.dnn.readNetFromDarknet()在python3上遇到的問題_李大狗的讀研日記-CSDN博客問題描述:代碼如下net = cv2.dnn.readNetFromDarknet(configPath,weightsPath)#獲取YOLO每一層的名稱#getLayerNames():獲取網(wǎng)絡(luò)所有層的名稱。ln = net.getLayerNames()# 獲取輸出層的名稱: [yolo-82,yolo-94,yolo-106]# getUnconnectedOutLayers():獲取輸出層的索引ln = [ln[i[0] - 1] for i in net.getUnconnhttps://blog.csdn.net/qq_39237205/article/details/121344325
正片如下
在這里我們進(jìn)行的目標(biāo)檢測是基于OPenCV的利用yoloV3進(jìn)行目標(biāo)檢測,不涉及yoloV3的模型結(jié)構(gòu)、理論及訓(xùn)練過程,只是利用訓(xùn)練好的模型進(jìn)行目標(biāo)檢測,整個(gè)流程如下:
基于OPenCV中的DNN模塊
- 加載已訓(xùn)練好的yolov3模型及其權(quán)重參數(shù)
- 將要處理的圖像轉(zhuǎn)換成輸入到模型中的blobs
- 利用模型對(duì)目標(biāo)進(jìn)行檢測
- 遍歷檢測結(jié)果
- 應(yīng)用非極大值抑制
- 繪制最終檢測結(jié)果,并存入到ndarray中,供目標(biāo)追蹤使用。
代碼如下:
1.加載yolov3模型及其權(quán)重參數(shù)
# 1.加載可以識(shí)別物體的名稱,將其存放在LABELS中,一共有80種,在這我們只使用car labelsPath = "./yolo-coco/coco.names" LABELS = open(labelsPath).read().strip().split("\n")# 設(shè)置隨機(jī)數(shù)種子,生成多種不同的顏色,當(dāng)一個(gè)畫面中有多個(gè)目標(biāo)時(shí),使用不同顏色的框?qū)⑵淇蚱饋?np.random.seed(42) COLORS = np.random.randint(0, 255, size=(200, 3),dtype="uint8")# 加載已訓(xùn)練好的yolov3網(wǎng)絡(luò)的權(quán)重和相應(yīng)的配置數(shù)據(jù) weightsPath = "./yolo-coco/yolov3.weights" configPath = "./yolo-coco/yolov3.cfg"# 加載好數(shù)據(jù)之后,開始利用上述數(shù)據(jù)恢復(fù)yolo神經(jīng)網(wǎng)絡(luò) net = cv2.dnn.readNetFromDarknet(configPath, weightsPath) # 獲取YOLO中每一網(wǎng)絡(luò)層的名稱:['conv_0', 'bn_0', 'relu_0', 'conv_1', 'bn_1', 'relu_1', 'conv_2', 'bn_2', 'relu_2'...] ln = net.getLayerNames() # 獲取輸出層在網(wǎng)絡(luò)中的索引位置,并以列表的形式:['yolo_82', 'yolo_94', 'yolo_106'] ln = [ln[i[0] - 1] for i in net.getUnconnectedOutLayers()]2.要處理的圖像轉(zhuǎn)換成輸入到模型中的blobs
# 2. 讀取圖像 frame = cv2.imread("./images/car1.jpg") # 視頻的寬度和高度,即幀尺寸 (W, H) = (None, None) if W is None or H is None:(H, W) = frame.shape[:2]# 根據(jù)輸入圖像構(gòu)造blob,利用OPenCV進(jìn)行深度網(wǎng)路的計(jì)算時(shí),一般將圖像轉(zhuǎn)換為blob形式,對(duì)圖片進(jìn)行預(yù)處理,包括縮放,減均值,通道交換等 # 還可以設(shè)置尺寸,一般設(shè)置為在進(jìn)行網(wǎng)絡(luò)訓(xùn)練時(shí)的圖像的大小 blob = cv2.dnn.blobFromImage(frame, 1 / 255.0, (416, 416), swapRB=True, crop=False)3.利用模型對(duì)目標(biāo)進(jìn)行檢測
# 3.將blob輸入到前向網(wǎng)絡(luò)中,并進(jìn)行預(yù)測 net.setInput(blob) start = time.time() # yolo前饋計(jì)算,獲取邊界和相應(yīng)的概率 # 輸出layerOutsputs介紹: # 是YOLO算法在圖片中檢測到的bbx的信息 # 由于YOLO v3有三個(gè)輸出,也就是上面提到的['yolo_82', 'yolo_94', 'yolo_106'] # 因此layerOutsputs是一個(gè)長度為3的列表 # 其中,列表中每一個(gè)元素的維度是(num_detection, 85) # num_detections表示該層輸出檢測到bbx的個(gè)數(shù) # 85:因?yàn)樵撃P驮贑OCO數(shù)據(jù)集上訓(xùn)練,[5:]表示類別概率;[0:4]表示bbx的位置信息;[5]表示置信度 layerOutputs = net.forward(ln)4.遍歷檢測結(jié)果,獲得檢測框
# 下面對(duì)網(wǎng)絡(luò)輸出的bbx進(jìn)行檢查: # 判定每一個(gè)bbx的置信度是否足夠的高,以及執(zhí)行NMS算法去除冗余的bbxboxes = [] # 用于存放識(shí)別物體的框的信息,包括框的左上角橫坐標(biāo)x和縱坐標(biāo)y以及框的高h(yuǎn)和寬w confidences = [] # 表示識(shí)別目標(biāo)是某種物體的可信度 classIDs = [] # 表示識(shí)別的目標(biāo)歸屬于哪一類,['person', 'bicycle', 'car', 'motorbike'....]# 4. 遍歷每一個(gè)輸出層的輸出 for output in layerOutputs:# 遍歷某個(gè)輸出層中的每一個(gè)目標(biāo)for detection in output:scores = detection[5:] # 當(dāng)前目標(biāo)屬于某一類別的概率classID = np.argmax(scores) # 目標(biāo)的類別IDconfidence = scores[classID] # 得到目標(biāo)屬于該類別的置信度# 只保留置信度大于0.3的邊界框,若圖片質(zhì)量較差,可以將置信度調(diào)低一點(diǎn)if confidence > 0.3:# 將邊界框的坐標(biāo)還原至與原圖片匹配,YOLO返回的是邊界框的中心坐標(biāo)以及邊界框的寬度和高度box = detection[0:4] * np.array([W, H, W, H])(centerX, centerY, width, height) = box.astype("int") # 使用 astype("int") 對(duì)上述 array 進(jìn)行強(qiáng)制類型轉(zhuǎn)換,centerX:框的中心點(diǎn)橫坐標(biāo), centerY:框的中心點(diǎn)縱坐標(biāo),width:框的寬度,height:框的高度x = int(centerX - (width / 2)) # 計(jì)算邊界框的左上角的橫坐標(biāo)y = int(centerY - (height / 2)) # 計(jì)算邊界框的左上角的縱坐標(biāo)# 更新檢測到的目標(biāo)框,置信度和類別IDboxes.append([x, y, int(width), int(height)]) # 將邊框的信息添加到列表boxesconfidences.append(float(confidence)) # 將識(shí)別出是某種物體的置信度添加到列表confidencesclassIDs.append(classID) # 將識(shí)別物體歸屬于哪一類的信息添加到列表classIDs5.非極大值抑制
# 5. 非極大值抑制 idxs = cv2.dnn.NMSBoxes(boxes, confidences, 0.5, 0.3)6.最終檢測結(jié)果,繪制,并存入到ndarray中,供目標(biāo)追蹤使用
# 6. 獲得最終的檢測結(jié)果 dets = [] # 存放檢測框的信息,包括左上角橫坐標(biāo),縱坐標(biāo),右下角橫坐標(biāo),縱坐標(biāo),以及檢測到的物體的置信度,用于目標(biāo)跟蹤 if len(idxs) > 0: # 存在檢測框的話(即檢測框個(gè)數(shù)大于0)for i in idxs.flatten(): # 循環(huán)檢測出的每一個(gè)box# yolo模型可以識(shí)別很多目標(biāo),因?yàn)槲覀冊(cè)谶@里只是識(shí)別車,所以只有目標(biāo)是車的我們進(jìn)行檢測,其他的忽略if LABELS[classIDs[i]] == "car":(x, y) = (boxes[i][0], boxes[i][1]) # 得到檢測框的左上角坐標(biāo)(w, h) = (boxes[i][2], boxes[i][3]) # 得到檢測框的寬和高cv2.rectangle(frame, (x, y), (x+w, y+h), (0,255,0), 2) # 將方框繪制在畫面上dets.append([x, y, x + w, y + h, confidences[i]]) # 將檢測框的信息的放入dets中 # 設(shè)置數(shù)據(jù)類型,將整型數(shù)據(jù)轉(zhuǎn)換為浮點(diǎn)數(shù)類型,且保留小數(shù)點(diǎn)后三位 np.set_printoptions(formatter={'float': lambda x: "{0:0.3f}".format(x)}) # 將檢測框數(shù)據(jù)轉(zhuǎn)換為ndarray,其數(shù)據(jù)類型為浮點(diǎn)型 dets = np.asarray(dets)plt.imshow(frame[:,:,::-1])在視頻中進(jìn)行目標(biāo)檢測:
labelsPath = "./yolo-coco/coco.names" LABELS = open(labelsPath).read().strip().split("\n")# 設(shè)置隨機(jī)數(shù)種子,生成多種不同的顏色,當(dāng)一個(gè)畫面中有多個(gè)目標(biāo)時(shí),使用不同顏色的框?qū)⑵淇蚱饋?np.random.seed(42) COLORS = np.random.randint(0, 255, size=(200, 3),dtype="uint8")# 加載已訓(xùn)練好的yolov3網(wǎng)絡(luò)的權(quán)重和相應(yīng)的配置數(shù)據(jù) weightsPath = "./yolo-coco/yolov3.weights" configPath = "./yolo-coco/yolov3.cfg"# 加載好數(shù)據(jù)之后,開始利用上述數(shù)據(jù)恢復(fù)yolo神經(jīng)網(wǎng)絡(luò) net = cv2.dnn.readNetFromDarknet(configPath, weightsPath) # 獲取YOLO中每一網(wǎng)絡(luò)層的名稱:['conv_0', 'bn_0', 'relu_0', 'conv_1', 'bn_1', 'relu_1', 'conv_2', 'bn_2', 'relu_2'...] ln = net.getLayerNames() # 獲取輸出層在網(wǎng)絡(luò)中的索引位置,并以列表的形式:['yolo_82', 'yolo_94', 'yolo_106'] ln = [ln[i[0] - 1] for i in net.getUnconnectedOutLayers()]""" 視頻處理類 """# 初始化vediocapture類,參數(shù)指定打開的視頻文件,也可以是攝像頭 vs = cv2.VideoCapture('./input/test_1.mp4') # 視頻的寬度和高度,即幀尺寸 (W, H) = (None, None) # 視頻文件寫對(duì)象 writer = Nonetry:# 確定獲取視頻幀數(shù)的方式prop = cv2.cv.CV_CAP_PROP_FRAME_COUNT if imutils.is_cv2() \else cv2.CAP_PROP_FRAME_COUNT# 獲取視頻的總幀數(shù)total = int(vs.get(prop))# 打印視頻的幀數(shù)print("[INFO] {} total frames in video".format(total)) except:print("[INFO] could not determine # of frames in video")print("[INFO] no approx. completion time can be provided")total = -1# 循環(huán)讀取視頻中的每一幀畫面 while True:# 讀取幀:grabbed是bool,表示是否成功捕獲幀,frame是捕獲的幀(grabbed, frame) = vs.read()# 若未捕獲幀,則退出循環(huán)if not grabbed:break# 若W和H為空,則將第一幀畫面的大小賦值給他if W is None or H is None:(H, W) = frame.shape[:2]# 根據(jù)輸入圖像構(gòu)造blob,利用OPenCV進(jìn)行深度網(wǎng)路的計(jì)算時(shí),一般將圖像轉(zhuǎn)換為blob形式,對(duì)圖片進(jìn)行預(yù)處理,包括縮放,減均值,通道交換等# 還可以設(shè)置尺寸,一般設(shè)置為在進(jìn)行網(wǎng)絡(luò)訓(xùn)練時(shí)的圖像的大小blob = cv2.dnn.blobFromImage(frame, 1 / 255.0, (416, 416), swapRB=True, crop=False)# 將blob輸入到前向網(wǎng)絡(luò)中net.setInput(blob)start = time.time()# yolo前饋計(jì)算,獲取邊界和相應(yīng)的概率layerOutputs = net.forward(ln)"""輸出layerOutsputs介紹:是YOLO算法在圖片中檢測到的bbx的信息由于YOLO v3有三個(gè)輸出,也就是上面提到的['yolo_82', 'yolo_94', 'yolo_106']因此layerOutsputs是一個(gè)長度為3的列表其中,列表中每一個(gè)元素的維度是(num_detection, 85)num_detections表示該層輸出檢測到bbx的個(gè)數(shù)85:因?yàn)樵撃P驮贑OCO數(shù)據(jù)集上訓(xùn)練,[5:]表示類別概率;[0:4]表示bbx的位置信息;[5]表示置信度"""end = time.time()"""下面對(duì)網(wǎng)絡(luò)輸出的bbx進(jìn)行檢查:判定每一個(gè)bbx的置信度是否足夠的高,以及執(zhí)行NMS算法去除冗余的bbx"""boxes = [] # 用于存放識(shí)別物體的框的信息,包括框的左上角橫坐標(biāo)x和縱坐標(biāo)y以及框的高h(yuǎn)和寬wconfidences = [] # 表示識(shí)別目標(biāo)是某種物體的可信度classIDs = [] # 表示識(shí)別的目標(biāo)歸屬于哪一類,['person', 'bicycle', 'car', 'motorbike'....]# 遍歷每一個(gè)輸出層的輸出for output in layerOutputs:# 遍歷某個(gè)輸出層中的每一個(gè)目標(biāo)for detection in output:scores = detection[5:] # 當(dāng)前目標(biāo)屬于某一類別的概率"""# scores = detection[5:] ---> [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.# 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.# 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.# 0. 0. 0. 0. 0. 0. 0. 0.]# scores的大小應(yīng)該是1*80,因?yàn)樵谟?xùn)練yolo模型時(shí)是80類目標(biāo)"""classID = np.argmax(scores) # 目標(biāo)的類別IDconfidence = scores[classID] # 得到目標(biāo)屬于該類別的置信度# 只保留置信度大于0.3的邊界框,若圖片質(zhì)量較差,可以將置信度調(diào)低一點(diǎn)if confidence > 0.3:# 將邊界框的坐標(biāo)還原至與原圖片匹配,YOLO返回的是邊界框的中心坐標(biāo)以及邊界框的寬度和高度box = detection[0:4] * np.array([W, H, W, H])(centerX, centerY, width, height) = box.astype("int") # 使用 astype("int") 對(duì)上述 array 進(jìn)行強(qiáng)制類型轉(zhuǎn)換,centerX:框的中心點(diǎn)橫坐標(biāo), centerY:框的中心點(diǎn)縱坐標(biāo),width:框的寬度,height:框的高度x = int(centerX - (width / 2)) # 計(jì)算邊界框的左上角的橫坐標(biāo)y = int(centerY - (height / 2)) # 計(jì)算邊界框的左上角的縱坐標(biāo)# 更新檢測到的目標(biāo)框,置信度和類別IDboxes.append([x, y, int(width), int(height)]) # 將邊框的信息添加到列表boxesconfidences.append(float(confidence)) # 將識(shí)別出是某種物體的置信度添加到列表confidencesclassIDs.append(classID) # 將識(shí)別物體歸屬于哪一類的信息添加到列表classIDs# 上一步中已經(jīng)得到y(tǒng)olo的檢測框,但其中會(huì)存在冗余的bbox,即一個(gè)目標(biāo)對(duì)應(yīng)多個(gè)檢測框,所以使用NMS去除重復(fù)的檢測框# 利用OpenCV內(nèi)置的NMS DNN模塊實(shí)現(xiàn)即可實(shí)現(xiàn)非最大值抑制 ,所需要的參數(shù)是邊界 框、 置信度、以及置信度閾值和NMS閾值# 第一個(gè)參數(shù)是存放邊界框的列表,第二個(gè)參數(shù)是存放置信度的列表,第三個(gè)參數(shù)是自己設(shè)置的置信度,第四個(gè)參數(shù)是關(guān)于threshold(閾值# 返回的idxs是一個(gè)一維數(shù)組,數(shù)組中的元素是保留下來的檢測框boxes的索引位置idxs = cv2.dnn.NMSBoxes(boxes, confidences, 0.5, 0.3)dets = [] # 存放檢測框的信息,包括左上角橫坐標(biāo),縱坐標(biāo),右下角橫坐標(biāo),縱坐標(biāo),以及檢測到的物體的置信度,用于目標(biāo)跟蹤if len(idxs) > 0: # 存在檢測框的話(即檢測框個(gè)數(shù)大于0)for i in idxs.flatten(): # 循環(huán)檢測出的每一個(gè)box# yolo模型可以識(shí)別很多目標(biāo),因?yàn)槲覀冊(cè)谶@里只是識(shí)別車,所以只有目標(biāo)是車的我們進(jìn)行檢測,其他的忽略if LABELS[classIDs[i]] == "car":(x, y) = (boxes[i][0], boxes[i][1]) # 得到檢測框的左上角坐標(biāo)(w, h) = (boxes[i][2], boxes[i][3]) # 得到檢測框的寬和高dets.append([x, y, x + w, y + h, confidences[i]]) # 將檢測框的信息的放入dets中# 設(shè)置數(shù)據(jù)類型,將整型數(shù)據(jù)轉(zhuǎn)換為浮點(diǎn)數(shù)類型,且保留小數(shù)點(diǎn)后三位np.set_printoptions(formatter={'float': lambda x: "{0:0.3f}".format(x)})# 將檢測框數(shù)據(jù)轉(zhuǎn)換為ndarray,其數(shù)據(jù)類型為浮點(diǎn)型dets = np.asarray(dets)總結(jié)
基于OPenCV的DNN模塊利用yoloV3模型進(jìn)行目標(biāo)檢測:
- 加載已訓(xùn)練好的yolov3模型及其權(quán)重參數(shù)
- 將要處理的圖像轉(zhuǎn)換成輸入到模型中的blobs
- 利用模型對(duì)目標(biāo)進(jìn)行檢測
- 遍歷檢測結(jié)果,應(yīng)用非極大值抑制
- 繪制最終檢測結(jié)果,并存入到ndarray中,供目標(biāo)追蹤使用。
yolo.py 【實(shí)現(xiàn)對(duì)圖片的目標(biāo)檢測】
# encoding:utf-8import imutils import time import cv2 import numpy as np import matplotlib.pyplot as plt #利用yolov3模型進(jìn)行目標(biāo)檢測 #加載模型相關(guān)信息 #加載可以檢測的目標(biāo)的類型#labelPath:類別標(biāo)簽文件的路徑 labelPath = "./yolo-coco/coco.names"# 加載類別標(biāo)簽文件 LABELS = open(labelPath).read().strip().split("\n")#生成多種不同的顏色的檢測框 用來標(biāo)注物體 np.random.seed(42) COLORS = np.random.randint(0,255,size=(200,3),dtype='uint8')#加載預(yù)訓(xùn)練的模型:權(quán)重 配置信息、進(jìn)行恢復(fù)模型 #weights_path:模型權(quán)重文件的路徑 weightsPath = "./yolo-coco/yolov3.weights" #configPath:模型配置文件的路徑 configPath = "./yolo-coco/yolov3.cfg"net = cv2.dnn.readNetFromDarknet(configPath,weightsPath) #獲取YOLO每一層的名稱 #getLayerNames():獲取網(wǎng)絡(luò)所有層的名稱。 ln = net.getLayerNames() # 獲取輸出層的名稱: [yolo-82,yolo-94,yolo-106] # getUnconnectedOutLayers():獲取輸出層的索引 ln = [ln[i[0] - 1] for i in net.getUnconnectedOutLayers()]#圖像的讀取 frame = cv2.imread('./images/car1.jpg') (W,H)=(None,None) (H,W)=frame.shape[:2]# 將圖片構(gòu)建成一個(gè)blob,設(shè)置圖片尺寸,然后執(zhí)行一次前向傳播 # YOLO前饋網(wǎng)絡(luò)計(jì)算,最終獲取邊界框和相應(yīng)概率 blob = cv2.dnn.blobFromImage(frame,1/255.0,(416,416),swapRB=True,crop=False) #將blob送入網(wǎng)絡(luò) net.setInput(blob) start = time.time() #前向傳播,進(jìn)行預(yù)測,返回目標(biāo)框的邊界和響應(yīng)的概率 layerOutouts = net.forward(ln) end = time.time()#存放目標(biāo)的檢測框 boxes = [] #置信度 confidences = [] #目標(biāo)類別 classIDs = []# 迭代每個(gè)輸出層,總共三個(gè) for output in layerOutouts:#遍歷每個(gè)檢測結(jié)果for detection in output:# 提取類別ID和置信度#detction:1*85 [5:]表示類別,[0:4]bbox的位置信息 [5]置信度、可信度scores = detection[5:]classID = np.argmax(scores)confidence= scores[classID]# 只保留置信度大于某值的邊界框if confidence >0.3:# 將邊界框的坐標(biāo)還原至與原圖片相匹配,記住YOLO返回的是邊界框的中心坐標(biāo)以及邊界框的寬度和高度box = detection[0:4] * np.array([W, H, W, H])(centerX,centerY,width,height) = box.astype("int")# 計(jì)算邊界框的左上角位置x = int(centerX-width/2)y = int(centerY-height/2)# 更新目標(biāo)框,置信度(概率)以及類別boxes.append([x,y,int(width),int(height)])confidences.append(float(confidence))classIDs.append(classID)# 使用非極大值抑制方法抑制弱、重疊的目標(biāo)框 idxs = cv2.dnn.NMSBoxes(boxes,confidences,0.5,0.3) #檢測框的結(jié)果:左上角坐標(biāo)、右下角坐標(biāo) dets = []# 確保至少有一個(gè)邊界框 if len(idxs)>0:# 迭代每個(gè)邊界框for i in idxs.flatten():# 提取邊界框的坐標(biāo)if LABELS[classIDs[i]] == "car":(x,y)=(boxes[i][0],boxes[i][1])(w,h)=(boxes[i][2],boxes[i][3])cv2.rectangle(frame,(x,y),(x+w,y+h),(0,255,0),2)dets.append([x,y,x+w,y+h,confidences[i]]) # 類型設(shè)置 np.set_printoptions(formatter={'float': lambda x: "{0:0.3f}".format(x)}) dets = np.asarray(dets)#顯示 plt.imshow(frame[:,:,::-1]) plt.show()結(jié)果顯示:
總結(jié)
以上是生活随笔為你收集整理的智慧交通day02-车流量检测实现12:基于yoloV3的目标检测的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 数据结构之图:加权无向图与寻找最小生成树
- 下一篇: 数据结构之堆:堆的排序,Python代码