一、首先将之前有关yolov3训练得到的模型文件以及模型配置文件和分类文件保存到新建的python工程文件夹目录下。也就是
yolov3-voc_3000.weights(模型权重文件)yolov3-voc.cfg(模型配置文件)voc.names(模型类别标签文件)。
二、代码详解
LABELS = open(labelsPath).read().strip().split("\n")
从模型分类标签当中得到数据集的类别,保存在列表当中。
In[6]: open(labelsPath).read()
Out[6]: 'cube\ncubiod\nhexagonal\ntriangular\n'
In[7]: open(labelsPath).read().strip()
Out[7]: 'cube\ncubiod\nhexagonal\ntriangular'
In[8]: open(labelsPath).read().strip().split('\n')
Out[8]: ['cube', 'cubiod', 'hexagonal', 'triangular']
声明三个数组:boxes 存放矩形框信息;confidences 存放框的置信度;classIDs 存放框的类别标签
三个数组元素一一对应,即boxes[0]、confidences[0]、classIDs[0]对应一个识别目标的信息,后期根据该信息在图片中画出识别目标的矩形框.
#加载 网络配置与训练的权重文件 构建网络
net = cv2.dnn.readNetFromDarknet(configPath, weightsPath)
需要至少opencv3及以上,opencv2中不支持dnn
#读入待检测的图像
image = cv2.imread('/home/darknet/scripts/VOCdevkit/VOC2019/JPEGImages/000770.jpg')
#得到图像的高和宽
(H,W) = image.shape[0:2]
然后得到yolo的输出层
ln = net.getLayerNames()
out = net.getUnconnectedOutLayers()
#得到未连接层得序号 [[200] /n [227] /n [254] ]
x = []
for i in out: # 1=[200]x.append(ln[i[0]-1])
# i[0]-1 取out中的数字 [200][0]=200 ln[199]= 'yolo_82'
ln=x
#从输入图像构造一个blob,然后通过加载的模型,给我们提供边界框和相关概率
#blobFromImage(image, scalefactor=None, size=None, mean=None, swapRB=None, crop=None, ddepth=None)
blob = cv2.dnn.blobFromImage(image, 1 / 255.0, (416, 416), swapRB=True, crop=False)#构造了一个blob图像,对原图像进行了图像的归一化 1 / 255.0,缩放尺寸 ,并对应训练模型时cfg的文件。
net.setInput(blob) #将blob设为输入??? 具体作用还不是很清楚
layerOutputs = net.forward(ln) #ln此时为输出层名称,向前传播 得到检测结果
在检测结果中会有很多每个类的置信度为0的矩形框,因此要把这些与置信度较低的框去掉。
for output in layerOutputs: #对三个输出层 循环for detection in output: #对每个输出层中的每个检测框循环scores=detection[5:] #detection=[x,y,h,w,c,class1,class2] scores取第6位至最后classID = np.argmax(scores)#np.argmax反馈最大值的索引confidence = scores[classID]if confidence >0.5:#过滤掉那些置信度较小的检测结果box = detection[0:4] * np.array([W, H, W, H])#print(box)(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)
接下来进行非极大值抑制的操作,这一操作的目的是过滤掉同一物体上的多个矩形框,opencv的dnn有个直接的函数
NMSBoxes(bboxes, scores, score_threshold, nms_threshold, eta=None, top_k=None)
bboxes需要操作的各矩形框对应程序的boxes
scores矩形框对应的置信度对应程序的confidences
score_threshold置信度的阈值,低于这个阈值的框直接删除
nms_threshold nms的阈值
下面简单说下非极大值抑制的原理
1)先对输入检测框按置信度由高到低排序
2)挑选第一个检测框(即最高置信度,记为A)和其它检测框(记为B)进行iou计算
关于IOU的解释看这里
3)如果iou大于nmsThreshold, 那就将B清除掉
4)跳转到2)从剩余得框集里面找置信度最大得框和其它框分别计算iou
5)直到所有框都过滤完
NMSBoxes()函数返回值为最终剩下的按置信度由高到低的矩形框的序列号
附上完整代码
#coding:utf-8
import numpy as np
import cv2
import osweightsPath='yolov3-voc_3000.weights'# 模型权重文件
configPath="yolov3-voc.cfg"# 模型配置文件
labelsPath = "voc.names"# 模型类别标签文件#初始化一些参数
LABELS = open(labelsPath).read().strip().split("\n")
COLORS = np.random.randint(0, 255, size=(len(LABELS), 3), dtype="uint8") # 随机生成颜色框boxes = []
confidences = []
classIDs = []#加载 网络配置与训练的权重文件 构建网络
net = cv2.dnn.readNetFromDarknet(configPath, weightsPath)#读入待检测的图像
image = cv2.imread('/home/jinyang/darknet/scripts/VOCdevkit/VOC2019/JPEGImages/000770.jpg')
#得到图像的高和宽
(H,W) = image.shape[0:2]# 得到 YOLO需要的输出层
ln = net.getLayerNames()
out = net.getUnconnectedOutLayers()#得到未连接层得序号 [[200] /n [267] /n [400] ]
x = []
for i in out: # 1=[200]x.append(ln[i[0]-1]) # i[0]-1 取out中的数字 [200][0]=200 ln(199)= 'yolo_82'
ln=x
# ln = ['yolo_82', 'yolo_94', 'yolo_106'] 得到 YOLO需要的输出层#从输入图像构造一个blob,然后通过加载的模型,给我们提供边界框和相关概率
#blobFromImage(image, scalefactor=None, size=None, mean=None, swapRB=None, crop=None, ddepth=None)
blob = cv2.dnn.blobFromImage(image, 1 / 255.0, (416, 416), swapRB=True, crop=False)#构造了一个blob图像,对原图像进行了图像的归一化,缩放了尺寸 ,对应训练模型
net.setInput(blob) #将blob设为输入??? 具体作用还不是很清楚
layerOutputs = net.forward(ln) #ln此时为输出层名称 ,向前传播 得到检测结果for output in layerOutputs: #对三个输出层 循环for detection in output: #对每个输出层中的每个检测框循环scores=detection[5:] #detection=[x,y,h,w,c,class1,class2] scores取第6位至最后classID = np.argmax(scores)#np.argmax反馈最大值的索引confidence = scores[classID]if confidence >0.5:#过滤掉那些置信度较小的检测结果box = detection[0:4] * np.array([W, H, W, H])#print(box)(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.2,0.3)
box_seq = idxs.flatten()#[ 2 9 7 10 6 5 4]if len(idxs)>0:for seq in box_seq:(x, y) = (boxes[seq][0], boxes[seq][1]) # 框左上角(w, h) = (boxes[seq][2], boxes[seq][3]) # 框宽高color = COLORS[classIDs[seq]].tolist()cv2.rectangle(image, (x, y), (x + w, y + h), color, 2) # 画框text = "{}: {:.4f}".format(LABELS[classIDs[seq]], confidences[seq])cv2.putText(image, text, (x, y - 5), cv2.FONT_HERSHEY_TRIPLEX, 1, color, 2) # 写字
cv2.namedWindow('Image', cv2.WINDOW_NORMAL)
cv2.imshow("Image", image)
cv2.waitKey(0)
问题:在运行代码后倘若出现以下这样的错误,则说明模型配置文件有问题,需要将配置文件作以下修改,即将cfg配置文件中的前四行全部屏蔽即可。
#Testing
#batch=1
#subdivisions=1
#Training
batch=64
subdivisions=16
net = cv2.dnn.readNetFromDarknet(configPath, weightsPath)
cv2.error: OpenCV(3.4.4) /io/opencv/modules/dnn/src/darknet/darknet_io.cpp:507: error: (-215:Assertion failed) separator_index < line.size() in function 'ReadDarknetFromCfgStream'
上述代码亲测可行,如有问题请留言,欢迎各位bloger共同探讨学习。
本篇博客参考
opencv调用yolov3模型进行目标检测,以实例进行代码详解_月照银海似蛟龙的博客-CSDN博客_opencv调用yolov3
其他关于opencv调用yolo模型进行检测的博客归纳如下:
OpenCV+yolov3实现目标检测(C++,Python)_pan_jinquan的博客-CSDN博客_c++实现目标检测
YOLOV3实战3:用python调用Darknet接口处理视频_phinoo的博客-CSDN博客_darknet python
基于OpenCV和YOLOv3深度学习的目标检测_qq_27158179的博客-CSDN博客_yolov3目标检测