当前位置:   article > 正文

【yolov3目标检测】(3) opencv+yolov3 检测交通路况,附python完整代码_depth = self.depth_image[int(yolo_center_point_y)]

depth = self.depth_image[int(yolo_center_point_y)][int(yolo_center_point_x)]

各位同学好,今天和大家分享一下如何使用 opencv 调用 yolov3 模型,加载网络权重,很方便地实现 yolov3 目标检测。先放张图看效果。

使用的网上找的行车记录仪视频做测试,数据集采用COCO数据集,检测效果还是不错的。


1. 预先准备

首先需要导入 COCO 数据集的分类名文件 'coco.names',以及yolov3的网络结构 'yolo.cfg',网络的权重参数 'yolo.weights',这些文件以及本案例的代码我给大家都提供好了,有需要的自取。

链接:https://pan.baidu.com/s/12iPJTjiN7SIBJ7hpHomn_w 

提取码:p548

yolov3 使用 Darknet53 网络模型,这个网络我之前复现过,感兴趣的可以看一下:https://blog.csdn.net/dgvv4/article/details/121997986

使用 cv2.dnn.readNetFromDarknet() 从opencv中读取网络模型,传入网络结构和权重参数。

由于我这个视频比较短,因此设置视频重复播放,使用 cv2.CAP_PROP_POS_FRAMES 获取当前视频所在第几帧,使用 cv2.CAP_PROP_FRAME_COUNT 获取该视频一共有多少帧。如果播放到了最后一帧,那就让当前帧=0,从头开始。

代码如下,net 配置完成,播放视频图像。

  1. import numpy as np
  2. import cv2
  3. import time
  4. #(1)加载预训练的COCO数据集
  5. classesFile = 'coco.names' # 指定coco数据集分类名所在路径
  6. classNames = [] # 创建列表,存放coco数据集的分类名称
  7. # 打开数据集名称的文件
  8. with open(classesFile, 'rt') as f: #读取文本文件
  9. classNames = f.read().rstrip('\n').split('\n') # 通过换行符来拆分,再读入
  10. # 加载yolov3结构cfg文件
  11. modelConfiguration = 'yolov3.cfg'
  12. # 加载yolov3网络权重
  13. modelWeights = 'yolov3.weights'
  14. #(2)构建网络结构
  15. # 导入darknet53网络,传入cfg文件和网络权重
  16. net = cv2.dnn.readNetFromDarknet(modelConfiguration, modelWeights)
  17. # 申明使用opencv作为后端
  18. net.setPreferableBackend(cv2.dnn.DNN_BACKEND_OPENCV)
  19. # 申明使用CPU计算
  20. net.setPreferableTarget(cv2.dnn.DNN_TARGET_CPU)
  21. #(3)获取摄像头
  22. videoFile = 'C:\\GameDownload\\Deep Learning\\yolov3video.mp4'
  23. cap = cv2.VideoCapture(videoFile) # 0代表电脑自带的摄像头,代表外接摄像头
  24. pTime = 0 # 设置第一帧开始处理的起始时间
  25. #(4)处理帧图像
  26. while True:
  27. # 接收图片是否导入成功、帧图像
  28. success, img = cap.read()
  29. # 改变图像大小
  30. img = cv2.resize(img, (1280,720))
  31. # 视频较短,循环播放
  32. if cap.get(cv2.CAP_PROP_POS_FRAMES) == cap.get(cv2.CAP_PROP_FRAME_COUNT):
  33. # 如果当前帧==总帧数,那就重置当前帧为0
  34. cap.set(cv2.CAP_PROP_POS_FRAMES, 0)
  35. #(5)显示图像
  36. # 查看FPS
  37. cTime = time.time() #处理完一帧图像的时间
  38. fps = 1/(cTime-pTime)
  39. pTime = cTime #重置起始时间
  40. # 在视频上显示fps信息,先转换成整数再变成字符串形式,文本显示坐标,文本字体,文本大小
  41. cv2.putText(img, str(int(fps)), (70,50), cv2.FONT_HERSHEY_PLAIN, 3, (255,0,0), 3)
  42. # 显示图像,输入窗口名及图像数据
  43. cv2.imshow('img', img)
  44. if cv2.waitKey(20) & 0xFF==27: #每帧滞留20毫秒后消失,ESC键退出
  45. break
  46. # 释放视频资源
  47. cap.release()
  48. cv2.destroyAllWindows()

2. 获取检测框信息

2.1 确定输入及输出

先看下面代码中的第(6)步,图像传入神经网络之前先进行预处理 cv2.dnn.blobFromImage(),包括减均值,比例缩放,裁剪,交换通道等,返回一个4通道的blob(blob可以简单理解为一个N维的数组)。之后使用 net.setInput() 将blob类型图像作为网络输入。

  1. cv2.dnn.blobFromImage(img, scalefactor, size, mean, swapRB, crop, ddepth)
  2. '''
  3. image: 输入图像
  4. scalefactor: 图像各通道数值的缩放比例,默认=1
  5. size: 输出图像的空间尺寸,如size=(200,300)表示高h=300,宽w=200
  6. mean: 用于各通道减去的值,以降低光照的影响,
  7. 例:(image为BGR的3通道的图像,mean=[104.0, 177.0, 123.0],表示B通道的值-104,G-177,R-123)
  8. swapRB: 交换RB通道,默认为False。(cv2.imread读取的是彩图是BGR通道)
  9. crop: 图像裁剪,默认为False。当值为True时,先按比例缩放,然后从中心裁剪成size尺寸
  10. ddepth: 输出的图像深度,可选CV_32F 或者 CV_8U.
  11. '''

由下图的 Darknet53 网络结构图可知,网络有三个输出层,输出的shape分别为 [52,52,255] 用于预测小目标[26,26,255] 用于预测中等大小的目标[13,13,255] 用于预测大目标。因此我们通过 net.getUnconnectedOutLayers() 就能知道这三个输出层处于网络中的第几层,得到的返回结果是 [200, 227, 254]通过 net.getLayerNames() 只要输入层的索引就能得到该索引所对应的层的名称。得到输出层的层名称后,将其传入 net.forward() 中,就能找到输出层的输出结果。

到这里就完成了网络模型的输入 blob,得到了模型的三个输出 outputs,打印输出结果的相关信息如下。以 outputs[0].shape =(300,85)为例, 300代表检测框的数量,85代表:中心点坐标 x,y;框的宽高 w,h;置信度 c;80 个分类各自的概率

  1. print(outputs[0].shape) # (300, 85)
  2. print(outputs[1].shape) # (1200, 85)
  3. print(outputs[2].shape) # (4800, 85)
  4. print(outputs[0][0]) # 打印第0个检测框所包含的信息
  5. '''
  6. (300, 85)
  7. (1200, 85)
  8. (4800, 85)
  9. [4.9195103e-02 5.5935599e-02 6.5290880e-01 1.8459144e-01 8.2010246e-08
  10. 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00
  11. 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00
  12. 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00
  13. 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00
  14. 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00
  15. 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00
  16. 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00
  17. 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00
  18. 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00
  19. 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00
  20. 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00
  21. 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00
  22. 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00
  23. 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00
  24. 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00
  25. 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00]
  26. '''

2.2 获取检测框信息

接下来我们就看到下面代码中的第(4)步,现在有了每个区域的预测结果 outputs,需要从中找到和每个物体对应的最合适的检测框。

注意一下,img.shape 是先指定图像的高,再指定宽,(h, w, c),不要搞错了。

其中 output 遍历三个输出层,每个输出层包含n个检测框,每个框包含85项信息det 遍历每一层的n个检测框,det 包含85项信息。我们需要找到每个框属于哪个分类,以及该框属于该分类的概率 confidence。如果该分类概率大于阈值 confThreshold 证明找到了,这时候就将该框的左上角坐标(x,y),框的宽w和高h,及置信度 confidence 保存下来

这里需要注意的是,每个框的85项信息中的其四个信息:中心坐标和宽高,都是归一化之后的比例坐标和比例宽高。需要将比例宽高乘上原图像宽高才能得到检测框的真实宽高

在第二节的代码中补充。

  1. import numpy as np
  2. import cv2
  3. import time
  4. #(1)加载预训练的COCO数据集
  5. classesFile = 'coco.names' # 指定coco数据集分类名所在路径
  6. classNames = [] # 创建列表,存放coco数据集的分类名称
  7. # 打开数据集名称的文件
  8. with open(classesFile, 'rt') as f: #读取文本文件
  9. classNames = f.read().rstrip('\n').split('\n') # 通过换行符来拆分,再读入
  10. # 加载yolov3结构cfg文件
  11. modelConfiguration = 'yolov3.cfg'
  12. # 加载yolov3网络权重
  13. modelWeights = 'yolov3.weights'
  14. # 确定输入图像的宽和高
  15. wInput, hInput = 320, 320
  16. # 自定义目标检测的最小置信度
  17. confThreshold = 0.5
  18. #(2)构建网络结构
  19. # 导入darknet53网络,传入cfg文件和网络权重
  20. net = cv2.dnn.readNetFromDarknet(modelConfiguration, modelWeights)
  21. # 申明使用opencv作为后端
  22. net.setPreferableBackend(cv2.dnn.DNN_BACKEND_OPENCV)
  23. # 申明使用CPU计算
  24. net.setPreferableTarget(cv2.dnn.DNN_TARGET_CPU)
  25. #(3)获取摄像头
  26. videoFile = 'C:\\GameDownload\\Deep Learning\\trafficvideo2.mp4'
  27. cap = cv2.VideoCapture(videoFile) # 0代表电脑自带的摄像头,代表外接摄像头
  28. pTime = 0 # 设置第一帧开始处理的起始时间
  29. #(4)定义函数用于检测目标,获取检测框信息,以及分类类别
  30. def findObjects(outputs, img):
  31. # 图像的高度、宽度、通道数
  32. hT, wT, cT = img.shape
  33. # 定义一个列表存放检测框的中心点坐标和宽高
  34. bbox = []
  35. # 定义列表存放分类的名称的索引
  36. classIds = []
  37. # 定义列表存放置信度
  38. confs = [] # 如果找到目标了,就将检测框的信息存放起来
  39. # 遍历三个输出层
  40. for output in outputs:
  41. # 遍历输出层的85项信息
  42. for det in output: # det是数组类型
  43. # 在80个分类中找到哪个分类的值是最高的
  44. score = det[5:] # 忽略检测框的x,y,w,h,c
  45. # 找到分类值最大对应的索引号
  46. classId = np.argmax(score)
  47. # 找到分类概率最大值的索引对应的值
  48. confidence = score[classId]
  49. # 如果检测置信度大于规定的阈值,表明检测到了物体
  50. if confidence > confThreshold:
  51. # 记录检测框的宽和高,这里的宽高是归一化之后的比例宽度和高度
  52. w, h = int(det[2]*wT), int(det[3]*hT) # 比例宽高转为像素宽高,像素宽高是整数
  53. # 记录检测框的左上角坐标
  54. x, y = det[0]*wT-w//2, det[1]*hT-h//2
  55. # 将检测框的信息保存起来
  56. bbox.append([x, y, w, h])
  57. # 将目标属于哪个类别的索引保存下来
  58. classIds.append(classId)
  59. # 保存检测框的置信度,检测出某个目标的概率
  60. confs.append(float(confidence))
  61. # 打印检测框信息
  62. print('classids:', classIds, 'confidence:', confs, 'bbox:', bbox)
  63. #(5)处理帧图像
  64. while True:
  65. # 接收图片是否导入成功、帧图像
  66. success, img = cap.read()
  67. # 改变图像大小
  68. img = cv2.resize(img, (1280,720))
  69. # 视频较短,循环播放
  70. if cap.get(cv2.CAP_PROP_POS_FRAMES) == cap.get(cv2.CAP_PROP_FRAME_COUNT):
  71. # 如果当前帧==总帧数,那就重置当前帧为0
  72. cap.set(cv2.CAP_PROP_POS_FRAMES, 0)
  73. #(6)将img图片转换成blob块类型,网络只接受这种类型
  74. # 输入img图像,,图像宽w高h,默认参数[0,0,0],,
  75. blob = cv2.dnn.blobFromImage(img, 1/255, (wInput,hInput), [0,0,0], 1, crop=False)
  76. # 将转换类型后的图像作为输入数据
  77. net.setInput(blob)
  78. # 获得网络各层的名称,由于网络会输出最后三层的结果,用于定位输出层
  79. layerNames = net.getLayerNames() # 得到网络所有层的名称
  80. # 提取输出层,返回输出层是第几层,层数是从1开始,索引是从0开始
  81. # net.getUnconnectedOutLayers() # [200, 227, 254]
  82. outputNames = [] # 存放输出层名称
  83. # 得到输出层在网络中属于第几层
  84. for outindex in net.getUnconnectedOutLayers():
  85. # 得到输出层的名称,outindex是第几层(从1开始),传入的是索引(从0开始)
  86. outputNames.append(layerNames[outindex-1])
  87. # 获取输出层返回结果
  88. outputs = net.forward(outputNames)
  89. print(outputs[0].shape) # (300, 85) 300代表检测框的数量,85代表:中心点坐标x,y,框的宽高w,h,置信度c,80个分类各自的概率
  90. print(outputs[1].shape) # (1200, 85)
  91. print(outputs[2].shape) # (4800, 85)
  92. print(outputs[0][0]) # 打印第0个检测框所包含的信息
  93. #(7)目标检测
  94. findObjects(outputs, img)
  95. #(8)显示图像
  96. # 查看FPS
  97. cTime = time.time() #处理完一帧图像的时间
  98. fps = 1/(cTime-pTime)
  99. pTime = cTime #重置起始时间
  100. # 在视频上显示fps信息,先转换成整数再变成字符串形式,文本显示坐标,文本字体,文本大小
  101. cv2.putText(img, str(int(fps)), (70,50), cv2.FONT_HERSHEY_PLAIN, 3, (255,0,0), 3)
  102. # 显示图像,输入窗口名及图像数据
  103. cv2.namedWindow("img", 0) # 窗口大小可调整
  104. cv2.imshow('img', img)
  105. if cv2.waitKey(20) & 0xFF==27: #每帧滞留20毫秒后消失,ESC键退出
  106. break
  107. # 释放视频资源
  108. cap.release()
  109. cv2.destroyAllWindows()

打印得到的检测框信息

  1. classids: [2, 7, 7, 2, 2, 2, 2, 2, 2]
  2. confidence: [0.8474083542823792, 0.6886539459228516, 0.7003887891769409, 0.9768345952033997, 0.7150803804397583, 0.7982962131500244, 0.7913717031478882, 0.9718230962753296, 0.8747612833976746]
  3. bbox: [[769.3665313720703, 398.1743869781494, 110, 88], [348.8137969970703, 387.51572608947754, 98, 70], [347.2083282470703, 388.56964111328125, 101, 71], [453.0018081665039, 409.030611038208, 32, 26], [625.3595275878906, 397.98907947540283, 67, 57], [681.5968627929688, 405.41797828674316, 37, 36], [694.1919250488281, 410.92028617858887, 37, 30], [760.8668212890625, 402.86146450042725, 124, 75], [777.7303924560547, 401.2068338394165, 112, 77]]

3. 显示预测框,完成目标检测

进行完上面的操作后,我们现在得到一个物体上可能有很多的检测框都满足条件,接下来采用 NMS 非极大值抑制 cv2.dnn.NMSBoxes()搜索出局部最大值,将置信度最大的框保存,其余剔除,确保每个目标至少有一个框。

  1. cv2.dnn.NMSBoxes(bboxes, scores, score_threshold, nms_threshold, eta=None, top_k=None)
  2. '''
  3. bboxes:检测框信息 [x,y,w,h]
  4. scores:每个待处理检测框的置信度
  5. score_threshold:用于过滤检测框的置信度阈值
  6. nms_threshold:NMS阈值
  7. eta:自适应阈值公式中的相关系数
  8. top_k: 如果 top_k>0,则保留最多 top_k 个边界框索引值
  9. '''

接下去就能使用矩形框绘制函数 cv2.rectangle() 把每个检测框绘制出来。

在上一节的代码中补充。

  1. import numpy as np
  2. import cv2
  3. import time
  4. #(1)加载预训练的COCO数据集
  5. classesFile = 'C:\\Users\\admin\\.spyder-py3\\test\\机器视觉\\yolov3\\coco.names' # 指定coco数据集分类名所在路径
  6. classNames = [] # 创建列表,存放coco数据集的分类名称
  7. # 打开数据集名称的文件
  8. with open(classesFile, 'rt') as f: #读取文本文件
  9. classNames = f.read().rstrip('\n').split('\n') # 通过换行符来拆分,再读入
  10. # 加载yolov3结构cfg文件
  11. modelConfiguration = 'yolov3.cfg'
  12. # 加载yolov3网络权重
  13. modelWeights = 'yolov3.weights'
  14. # 确定输入图像的宽和高
  15. wInput, hInput = 320, 320
  16. # 自定义目标检测的最小置信度
  17. confThreshold = 0.5
  18. # 自定义非极大值抑制的参数
  19. nms_threshold = 0.3
  20. #(2)构建网络结构
  21. # 导入darknet53网络,传入cfg文件和网络权重
  22. net = cv2.dnn.readNetFromDarknet(modelConfiguration, modelWeights)
  23. # 申明使用opencv作为后端
  24. net.setPreferableBackend(cv2.dnn.DNN_BACKEND_OPENCV)
  25. # 申明使用CPU计算
  26. net.setPreferableTarget(cv2.dnn.DNN_TARGET_CPU)
  27. #(3)获取摄像头
  28. videoFile = 'C:\\GameDownload\\Deep Learning\\trafficvideo1.mp4'
  29. cap = cv2.VideoCapture(videoFile) # 0代表电脑自带的摄像头,代表外接摄像头
  30. #(4)定义函数用于检测目标,获取检测框信息,以及分类类别
  31. def findObjects(outputs, img):
  32. # 图像的高度、宽度、通道数
  33. hT, wT, cT = img.shape # 先保存高度,再保存宽度
  34. # 定义一个列表存放检测框的中心点坐标和宽高
  35. bbox = []
  36. # 定义列表存放分类的名称的索引
  37. classIds = []
  38. # 定义列表存放置信度
  39. confs = [] # 如果找到目标了,就将检测框的信息存放起来
  40. # 遍历三个输出层
  41. for output in outputs:
  42. # 遍历输出层的85项信息
  43. for det in output: # det是数组类型
  44. # 在80个分类中找到哪个分类的值是最高的
  45. score = det[5:] # 忽略检测框的x,y,w,h,c
  46. # 找到分类值最大对应的索引号
  47. classId = np.argmax(score)
  48. # 找到分类概率最大值的索引对应的值
  49. confidence = score[classId]
  50. # 如果检测置信度大于规定的阈值,表明检测到了物体
  51. if confidence > confThreshold:
  52. # 记录检测框的宽和高,这里的宽高是归一化之后的比例宽度和高度
  53. w, h = int(det[2]*wT), int(det[3]*hT) # 比例宽高转为像素宽高,像素宽高是整数
  54. # 记录检测框的左上角坐标
  55. x, y = int(det[0]*wT-w/2), int(det[1]*hT-h/2)
  56. # 将检测框的信息保存起来
  57. bbox.append([x, y, w, h])
  58. # 将目标属于哪个类别的索引保存下来
  59. classIds.append(classId)
  60. # 保存检测框的置信度,检测出某个目标的概率
  61. confs.append(float(confidence))
  62. #(5)消除重叠的矩形框,非极大值抑制
  63. indices = cv2.dnn.NMSBoxes(bbox, confs, confThreshold, nms_threshold) # 返回检测框的索引
  64. # 遍历索引绘制矩形框
  65. for i in indices:
  66. # 在所有包含目标的矩形框中找到最符合的矩形框
  67. box = bbox[i]
  68. # 提取矩形框的信息
  69. x, y, w, h = box[0], box[1], box[2], box[3]
  70. # 绘制矩形框
  71. cv2.rectangle(img, (x,y), (x+w,y+h), (255,255,0), 2)
  72. # 显示文本
  73. cv2.putText(img, f'{classNames[classIds[i]]}',
  74. (x,y+h+18), cv2.FONT_HERSHEY_COMPLEX, 0.8, (0,255,0), 2)
  75. cv2.putText(img, f'{int(confs[i]*100)}%',
  76. (x,y-8), cv2.FONT_HERSHEY_COMPLEX, 0.8, (0,0,255), 2)
  77. #(6)处理帧图像
  78. while True:
  79. # 接收图片是否导入成功、帧图像
  80. success, img = cap.read()
  81. # 改变图像大小
  82. img = cv2.resize(img, (1280,720))
  83. # 视频较短,循环播放
  84. if cap.get(cv2.CAP_PROP_POS_FRAMES) == cap.get(cv2.CAP_PROP_FRAME_COUNT):
  85. # 如果当前帧==总帧数,那就重置当前帧为0
  86. cap.set(cv2.CAP_PROP_POS_FRAMES, 0)
  87. #(7)将img图片转换成blob块类型,网络只接受这种类型
  88. # 输入img图像,,图像宽w高h,默认参数[0,0,0],,
  89. blob = cv2.dnn.blobFromImage(img, 1/255, (wInput,hInput), [0,0,0], 1, crop=False)
  90. # 将转换类型后的图像作为输入数据
  91. net.setInput(blob)
  92. # 获得网络各层的名称,由于网络会输出最后三层的结果,用于定位输出层
  93. layerNames = net.getLayerNames() # 得到网络所有层的名称
  94. # 提取输出层,返回输出层是第几层,层数是从1开始,索引是从0开始
  95. # net.getUnconnectedOutLayers() # [200, 227, 254]
  96. outputNames = [] # 存放输出层名称
  97. # 得到输出层在网络中属于第几层
  98. for outindex in net.getUnconnectedOutLayers():
  99. # 得到输出层的名称,outindex是第几层(从1开始),传入的是索引(从0开始)
  100. outputNames.append(layerNames[outindex-1])
  101. # 获取输出层返回结果
  102. outputs = net.forward(outputNames)
  103. #(8)目标检测
  104. findObjects(outputs, img)
  105. #(9)显示图像
  106. # 显示图像,输入窗口名及图像数据
  107. cv2.namedWindow("img", 0) # 窗口大小可调整
  108. cv2.imshow('img', img)
  109. if cv2.waitKey(1) & 0xFF==27: #每帧滞留20毫秒后消失,ESC键退出
  110. break
  111. # 释放视频资源
  112. cap.release()
  113. cv2.destroyAllWindows()

检测结果如下:

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/不正经/article/detail/474083
推荐阅读
相关标签
  

闽ICP备14008679号