在本章节代码编写中,发现之前的代码所处的环境是python3,因此导致了cv2.dnn.readNetFromDarknet()在代码运行中导致了i[0]的获值失败,故总结如下:
cv2.dnn.readNetFromDarknet()在python3上遇到的问题_李大狗的读研日记-CSDN博客
问题描述:代码如下net = cv2.dnn.readNetFromDarknet(configPath,weightsPath)#获取YOLO每一层的名称#getLayerNames():获取网络所有层的名称。ln = net.getLayerNames()# 获取输出层的名称: [yolo-82,yolo-94,yolo-106]# getUnconnectedOutLayers():获取输出层的索引ln = [ln[i[0] - 1] for i in net.getUnconn
https://blog.csdn.net/qq_39237205/article/details/121344325
正片如下
在这里我们进行的目标检测是基于OPenCV的利用yoloV3进行目标检测,不涉及yoloV3的模型结构、理论及训练过程,只是利用训练好的模型进行目标检测,整个流程如下:
基于OPenCV中的DNN模块
- 加载已训练好的yolov3模型及其权重参数
- 将要处理的图像转换成输入到模型中的blobs
- 利用模型对目标进行检测
- 遍历检测结果
- 应用非极大值抑制
- 绘制最终检测结果,并存入到ndarray中,供目标追踪使用。
代码如下:
1.加载yolov3模型及其权重参数
# 1.加载可以识别物体的名称,将其存放在LABELS中,一共有80种,在这我们只使用car labelsPath = "./yolo-coco/coco.names" LABELS = open(labelsPath).read().strip().split("\n") # 设置随机数种子,生成多种不同的颜色,当一个画面中有多个目标时,使用不同颜色的框将其框起来 np.random.seed(42) COLORS = np.random.randint(0, 255, size=(200, 3),dtype="uint8") # 加载已训练好的yolov3网络的权重和相应的配置数据 weightsPath = "./yolo-coco/yolov3.weights" configPath = "./yolo-coco/yolov3.cfg" # 加载好数据之后,开始利用上述数据恢复yolo神经网络 net = cv2.dnn.readNetFromDarknet(configPath, weightsPath) # 获取YOLO中每一网络层的名称:['conv_0', 'bn_0', 'relu_0', 'conv_1', 'bn_1', 'relu_1', 'conv_2', 'bn_2', 'relu_2'...] ln = net.getLayerNames() # 获取输出层在网络中的索引位置,并以列表的形式:['yolo_82', 'yolo_94', 'yolo_106'] ln = [ln[i[0] - 1] for i in net.getUnconnectedOutLayers()]
2.要处理的图像转换成输入到模型中的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] # 根据输入图像构造blob,利用OPenCV进行深度网路的计算时,一般将图像转换为blob形式,对图片进行预处理,包括缩放,减均值,通道交换等 # 还可以设置尺寸,一般设置为在进行网络训练时的图像的大小 blob = cv2.dnn.blobFromImage(frame, 1 / 255.0, (416, 416), swapRB=True, crop=False)
3.利用模型对目标进行检测
# 3.将blob输入到前向网络中,并进行预测 net.setInput(blob) start = time.time() # yolo前馈计算,获取边界和相应的概率 # 输出layerOutsputs介绍: # 是YOLO算法在图片中检测到的bbx的信息 # 由于YOLO v3有三个输出,也就是上面提到的['yolo_82', 'yolo_94', 'yolo_106'] # 因此layerOutsputs是一个长度为3的列表 # 其中,列表中每一个元素的维度是(num_detection, 85) # num_detections表示该层输出检测到bbx的个数 # 85:因为该模型在COCO数据集上训练,[5:]表示类别概率;[0:4]表示bbx的位置信息;[5]表示置信度 layerOutputs = net.forward(ln)
4.遍历检测结果,获得检测框
# 下面对网络输出的bbx进行检查: # 判定每一个bbx的置信度是否足够的高,以及执行NMS算法去除冗余的bbx boxes = [] # 用于存放识别物体的框的信息,包括框的左上角横坐标x和纵坐标y以及框的高h和宽w confidences = [] # 表示识别目标是某种物体的可信度 classIDs = [] # 表示识别的目标归属于哪一类,['person', 'bicycle', 'car', 'motorbike'....] # 4. 遍历每一个输出层的输出 for output in layerOutputs: # 遍历某个输出层中的每一个目标 for detection in output: scores = detection[5:] # 当前目标属于某一类别的概率 classID = np.argmax(scores) # 目标的类别ID confidence = scores[classID] # 得到目标属于该类别的置信度 # 只保留置信度大于0.3的边界框,若图片质量较差,可以将置信度调低一点 if confidence > 0.3: # 将边界框的坐标还原至与原图片匹配,YOLO返回的是边界框的中心坐标以及边界框的宽度和高度 box = detection[0:4] * np.array([W, H, W, H]) (centerX, centerY, width, height) = box.astype("int") # 使用 astype("int") 对上述 array 进行强制类型转换,centerX:框的中心点横坐标, centerY:框的中心点纵坐标,width:框的宽度,height:框的高度 x = int(centerX - (width / 2)) # 计算边界框的左上角的横坐标 y = int(centerY - (height / 2)) # 计算边界框的左上角的纵坐标 # 更新检测到的目标框,置信度和类别ID boxes.append([x, y, int(width), int(height)]) # 将边框的信息添加到列表boxes confidences.append(float(confidence)) # 将识别出是某种物体的置信度添加到列表confidences classIDs.append(classID) # 将识别物体归属于哪一类的信息添加到列表classIDs
5.非极大值抑制
# 5. 非极大值抑制 idxs = cv2.dnn.NMSBoxes(boxes, confidences, 0.5, 0.3)
6.最终检测结果,绘制,并存入到ndarray中,供目标追踪使用
# 6. 获得最终的检测结果 dets = [] # 存放检测框的信息,包括左上角横坐标,纵坐标,右下角横坐标,纵坐标,以及检测到的物体的置信度,用于目标跟踪 if len(idxs) > 0: # 存在检测框的话(即检测框个数大于0) for i in idxs.flatten(): # 循环检测出的每一个box # yolo模型可以识别很多目标,因为我们在这里只是识别车,所以只有目标是车的我们进行检测,其他的忽略 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]]) # 将检测框的信息的放入dets中 # 设置数据类型,将整型数据转换为浮点数类型,且保留小数点后三位 np.set_printoptions(formatter={'float': lambda x: "{0:0.3f}".format(x)}) # 将检测框数据转换为ndarray,其数据类型为浮点型 dets = np.asarray(dets) plt.imshow(frame[:,:,::-1])
在视频中进行目标检测:
labelsPath = "./yolo-coco/coco.names" LABELS = open(labelsPath).read().strip().split("\n") # 设置随机数种子,生成多种不同的颜色,当一个画面中有多个目标时,使用不同颜色的框将其框起来 np.random.seed(42) COLORS = np.random.randint(0, 255, size=(200, 3),dtype="uint8") # 加载已训练好的yolov3网络的权重和相应的配置数据 weightsPath = "./yolo-coco/yolov3.weights" configPath = "./yolo-coco/yolov3.cfg" # 加载好数据之后,开始利用上述数据恢复yolo神经网络 net = cv2.dnn.readNetFromDarknet(configPath, weightsPath) # 获取YOLO中每一网络层的名称:['conv_0', 'bn_0', 'relu_0', 'conv_1', 'bn_1', 'relu_1', 'conv_2', 'bn_2', 'relu_2'...] ln = net.getLayerNames() # 获取输出层在网络中的索引位置,并以列表的形式:['yolo_82', 'yolo_94', 'yolo_106'] ln = [ln[i[0] - 1] for i in net.getUnconnectedOutLayers()] """ 视频处理类 """ # 初始化vediocapture类,参数指定打开的视频文件,也可以是摄像头 vs = cv2.VideoCapture('./input/test_1.mp4') # 视频的宽度和高度,即帧尺寸 (W, H) = (None, None) # 视频文件写对象 writer = None try: # 确定获取视频帧数的方式 prop = cv2.cv.CV_CAP_PROP_FRAME_COUNT if imutils.is_cv2() \ else cv2.CAP_PROP_FRAME_COUNT # 获取视频的总帧数 total = int(vs.get(prop)) # 打印视频的帧数 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 # 循环读取视频中的每一帧画面 while True: # 读取帧:grabbed是bool,表示是否成功捕获帧,frame是捕获的帧 (grabbed, frame) = vs.read() # 若未捕获帧,则退出循环 if not grabbed: break # 若W和H为空,则将第一帧画面的大小赋值给他 if W is None or H is None: (H, W) = frame.shape[:2] # 根据输入图像构造blob,利用OPenCV进行深度网路的计算时,一般将图像转换为blob形式,对图片进行预处理,包括缩放,减均值,通道交换等 # 还可以设置尺寸,一般设置为在进行网络训练时的图像的大小 blob = cv2.dnn.blobFromImage(frame, 1 / 255.0, (416, 416), swapRB=True, crop=False) # 将blob输入到前向网络中 net.setInput(blob) start = time.time() # yolo前馈计算,获取边界和相应的概率 layerOutputs = net.forward(ln) """ 输出layerOutsputs介绍: 是YOLO算法在图片中检测到的bbx的信息 由于YOLO v3有三个输出,也就是上面提到的['yolo_82', 'yolo_94', 'yolo_106'] 因此layerOutsputs是一个长度为3的列表 其中,列表中每一个元素的维度是(num_detection, 85) num_detections表示该层输出检测到bbx的个数 85:因为该模型在COCO数据集上训练,[5:]表示类别概率;[0:4]表示bbx的位置信息;[5]表示置信度 """ end = time.time() """ 下面对网络输出的bbx进行检查: 判定每一个bbx的置信度是否足够的高,以及执行NMS算法去除冗余的bbx """ boxes = [] # 用于存放识别物体的框的信息,包括框的左上角横坐标x和纵坐标y以及框的高h和宽w confidences = [] # 表示识别目标是某种物体的可信度 classIDs = [] # 表示识别的目标归属于哪一类,['person', 'bicycle', 'car', 'motorbike'....] # 遍历每一个输出层的输出 for output in layerOutputs: # 遍历某个输出层中的每一个目标 for detection in output: scores = detection[5:] # 当前目标属于某一类别的概率 """ # 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的大小应该是1*80,因为在训练yolo模型时是80类目标 """ classID = np.argmax(scores) # 目标的类别ID confidence = scores[classID] # 得到目标属于该类别的置信度 # 只保留置信度大于0.3的边界框,若图片质量较差,可以将置信度调低一点 if confidence > 0.3: # 将边界框的坐标还原至与原图片匹配,YOLO返回的是边界框的中心坐标以及边界框的宽度和高度 box = detection[0:4] * np.array([W, H, W, H]) (centerX, centerY, width, height) = box.astype("int") # 使用 astype("int") 对上述 array 进行强制类型转换,centerX:框的中心点横坐标, centerY:框的中心点纵坐标,width:框的宽度,height:框的高度 x = int(centerX - (width / 2)) # 计算边界框的左上角的横坐标 y = int(centerY - (height / 2)) # 计算边界框的左上角的纵坐标 # 更新检测到的目标框,置信度和类别ID boxes.append([x, y, int(width), int(height)]) # 将边框的信息添加到列表boxes confidences.append(float(confidence)) # 将识别出是某种物体的置信度添加到列表confidences classIDs.append(classID) # 将识别物体归属于哪一类的信息添加到列表classIDs # 上一步中已经得到yolo的检测框,但其中会存在冗余的bbox,即一个目标对应多个检测框,所以使用NMS去除重复的检测框 # 利用OpenCV内置的NMS DNN模块实现即可实现非最大值抑制 ,所需要的参数是边界 框、 置信度、以及置信度阈值和NMS阈值 # 第一个参数是存放边界框的列表,第二个参数是存放置信度的列表,第三个参数是自己设置的置信度,第四个参数是关于threshold(阈值 # 返回的idxs是一个一维数组,数组中的元素是保留下来的检测框boxes的索引位置 idxs = cv2.dnn.NMSBoxes(boxes, confidences, 0.5, 0.3) dets = [] # 存放检测框的信息,包括左上角横坐标,纵坐标,右下角横坐标,纵坐标,以及检测到的物体的置信度,用于目标跟踪 if len(idxs) > 0: # 存在检测框的话(即检测框个数大于0) for i in idxs.flatten(): # 循环检测出的每一个box # yolo模型可以识别很多目标,因为我们在这里只是识别车,所以只有目标是车的我们进行检测,其他的忽略 if LABELS[classIDs[i]] == "car": (x, y) = (boxes[i][0], boxes[i][1]) # 得到检测框的左上角坐标 (w, h) = (boxes[i][2], boxes[i][3]) # 得到检测框的宽和高 dets.append([x, y, x + w, y + h, confidences[i]]) # 将检测框的信息的放入dets中 # 设置数据类型,将整型数据转换为浮点数类型,且保留小数点后三位 np.set_printoptions(formatter={'float': lambda x: "{0:0.3f}".format(x)}) # 将检测框数据转换为ndarray,其数据类型为浮点型 dets = np.asarray(dets)
总结
基于OPenCV的DNN模块利用yoloV3模型进行目标检测:
- 加载已训练好的yolov3模型及其权重参数
- 将要处理的图像转换成输入到模型中的blobs
- 利用模型对目标进行检测
- 遍历检测结果,应用非极大值抑制
- 绘制最终检测结果,并存入到ndarray中,供目标追踪使用。
yolo.py 【实现对图片的目标检测】
# encoding:utf-8 import imutils import time import cv2 import numpy as np import matplotlib.pyplot as plt #利用yolov3模型进行目标检测 #加载模型相关信息 #加载可以检测的目标的类型 #labelPath:类别标签文件的路径 labelPath = "./yolo-coco/coco.names" # 加载类别标签文件 LABELS = open(labelPath).read().strip().split("\n") #生成多种不同的颜色的检测框 用来标注物体 np.random.seed(42) COLORS = np.random.randint(0,255,size=(200,3),dtype='uint8') #加载预训练的模型:权重 配置信息、进行恢复模型 #weights_path:模型权重文件的路径 weightsPath = "./yolo-coco/yolov3.weights" #configPath:模型配置文件的路径 configPath = "./yolo-coco/yolov3.cfg" net = cv2.dnn.readNetFromDarknet(configPath,weightsPath) #获取YOLO每一层的名称 #getLayerNames():获取网络所有层的名称。 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] # 将图片构建成一个blob,设置图片尺寸,然后执行一次前向传播 # YOLO前馈网络计算,最终获取边界框和相应概率 blob = cv2.dnn.blobFromImage(frame,1/255.0,(416,416),swapRB=True,crop=False) #将blob送入网络 net.setInput(blob) start = time.time() #前向传播,进行预测,返回目标框的边界和响应的概率 layerOutouts = net.forward(ln) end = time.time() #存放目标的检测框 boxes = [] #置信度 confidences = [] #目标类别 classIDs = [] # 迭代每个输出层,总共三个 for output in layerOutouts: #遍历每个检测结果 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: # 将边界框的坐标还原至与原图片相匹配,记住YOLO返回的是边界框的中心坐标以及边界框的宽度和高度 box = detection[0:4] * np.array([W, H, W, H]) (centerX,centerY,width,height) = box.astype("int") # 计算边界框的左上角位置 x = int(centerX-width/2) y = int(centerY-height/2) # 更新目标框,置信度(概率)以及类别 boxes.append([x,y,int(width),int(height)]) confidences.append(float(confidence)) classIDs.append(classID) # 使用非极大值抑制方法抑制弱、重叠的目标框 idxs = cv2.dnn.NMSBoxes(boxes,confidences,0.5,0.3) #检测框的结果:左上角坐标、右下角坐标 dets = [] # 确保至少有一个边界框 if len(idxs)>0: # 迭代每个边界框 for i in idxs.flatten(): # 提取边界框的坐标 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]]) # 类型设置 np.set_printoptions(formatter={'float': lambda x: "{0:0.3f}".format(x)}) dets = np.asarray(dets) #显示 plt.imshow(frame[:,:,::-1]) plt.show()