• opencv项目9---利用YOLOv3实现对物体的实时检测


    #利用yolov3的模型结构和权重参数实现对物体的实时检测,正确率挺高的,其主要原理是利用神经网络去将我们的图像不断的进行处理,最后利用图像处理中的金字塔思想,做了3次采样变化,得到不同的特征图,通过用3种不同的方式进行预测,判断处最合适的预测,并将结果返回。有兴趣的可以去看看相关的论文。

    1.代码运行后展示不同物体的结果(使用手机的图片)

     

     

     总的来说,检测的效果不错,但在我运行过程中,出现了实时视频的卡顿,不流畅的问题,可能是因为我使用的是CPU,处理的速度跟不上,有兴趣的可以用GPU试一下。官方网站显示的有相关的fps标准,可能是设备问题,所以帧率没有达到45fps.关于权重文件和模型配置,可以去官网下载,网址:

    https://pjreddie.com/darknet/yolo/

    你使用对应的yolov3d的model就要下载对应的权重文件和模型配置,不一样的模型内部的神经网络层也不太一样,以yolov3-320,对应的输出特征图像是3个,而yolov3-tiny对应的输出特征图像是2个。当然精度和速度不能权衡,两个之间一个好,另一个就必然下降。

    如图:

    我自己尝试了320和tiny的模型,有兴趣的可以尝试不一样的model。

    当下载好对应的权重参数和模型配置后,还要下载coco.name数据集,里面有80个常见的物体种类的名字,模型会根据预测返回对每个种类的概率。

    coco.name数据集我已经上传到github上:

    https://github.com/Drift-Of-Little-Forest/opencv-practice.git

    下面就是代码的复现了:

    通过以下代码,实现对数据集的读取,并以列表形式展示出来,方便我们进行索引

    1. ## 导入coco数据集,里面有各种80多种类别
    2. classesFile = "opencv_data_ku/coco.names"
    3. classNames = []
    4. with open(classesFile, 'rt') as f:
    5. classNames = f.read().rstrip('\n').split('\n')
    6. print(classNames)

    然后就是导入神经网络模型:

    1. "===============引入我们的yolo3模块=========="
    2. ## Model Files
    3. #可以去yolo官网自己搜下面相关的文件参数
    4. modelConfiguration = "yolov3-320.cfg"
    5. modelWeights = "opencv_data_ku/yolov3.weights"
    6. #调用函数进行模型的配置和权重的配置
    7. net = cv.dnn.readNetFromDarknet(modelConfiguration, modelWeights)
    8. #要求网络使用其支持的特定计算后
    9. net.setPreferableBackend(cv.dnn.DNN_BACKEND_OPENCV)
    10. #使用CPU进行计算
    11. net.setPreferableTarget(cv.dnn.DNN_TARGET_CPU)

    紧接着就是调动相机,进行获取实时图像:

    1. cap = cv.VideoCapture(0)
    2. while True:
    3. success, img = cap.read()
    4. img = cv2.flip(img, 1)
    5. cv.imshow('Image', img)
    6. cv.waitKey(1)

    上面的代码中:

    img = cv2.flip(img, 1)

    是让图像实现镜像,左右跟我们实时的一致。

    然后就是将实时获取的图像传输就我们的模型当中:

    1. #神经网络的输入图像需要采用称为blob的特定格式。用需要检测的原始图像image构造一个blob图像,
    2. #对原图像进行像素归一化1 / 255.0,缩放尺寸
    3. # (320, 320),交换了R与B通道
    4. blob = cv.dnn.blobFromImage(img, 1 / 255, (320, 320), 1, crop=False)
    5. #将blob变成网络的输入
    6. net.setInput(blob)
    7. #获取神经网络所有层的名称
    8. layersNames = net.getLayerNames()
    9. print('所有层:',layersNames)
    10. #我们不需要改变其他层,只需要找到未连接层,也就是网络的最后一层,在最后一层的基础上进行前向传播就可以了
    11. print('三个输出层的索引号',net.getUnconnectedOutLayers())
    12. for i in net.getUnconnectedOutLayers():
    13. outputNames = [layersNames[i - 1]]
    14. print('输出层名字',outputNames)
    15. #前向传播
    16. outputs = net.forward(outputNames)
    17. print(outputs)
    18. #了解每个输出层的形状
    19. print(outputs[0].shape)

    通过上面的操作,我们已经可以获得3个输出层的内容,例如(300,85),(1200,85),(4800,85),就是对不同特征图像的每一个特征像素的判断,85列中的内容大概如下:

     可以清楚看到,前5项,分别是(x,y,w,h,conf),也就是预测框的中心点和矩形的长宽,后面一个是置信度。后面的是对种类的预测概率,我们可以通过对列表的索引,切片等操作,把这些信息给提取出来,有了这些我们就可以用opencv 中的

    cv.rectangle,cv.putText

    在图像中画出对应的框和标注对应的内容。需要注意的是opencv中画矩形框,是需要定义框的起始位置和窗高的,所以我们要进行转换:(前5项,分别是(x,y,w,h,conf),也就是预测框的中心点和矩形的长宽,后面一个是置信度,这是在一个像素点上的,如果是放在整张图像上呢?,肯定要乘以图像的宽和高,可以看我画的示意图:)

    具体代码操作:

    1. w, h = int(det[2] * wT), int(det[3] * hT)
    2. x, y = int((det[0] * wT) - w / 2), int((det[1] * hT) - h / 2)

    这样就可以画出我们对预测对象的框了,但是我们数据是300行,1200行,4800行,所以我们要进行遍历.........具体代码如下:

    1. def findObjects(outputs, img):
    2. hT, wT, cT = img.shape#输出照片的宽高通道数
    3. bbox = []
    4. classIds = []
    5. confs = []
    6. #对检测处的结果进行对比处理
    7. for output in outputs:
    8. for det in output:
    9. #可以看一下输出的内容
    10. scores = det[5:]#这里的意思是输出是某个种类的概率,前五个是框的位置以及置信度
    11. classId = np.argmax(scores)#找到是最大的种类编号
    12. confidence = scores[classId]#找到置信度
    13. if confidence > 0.5:#设立阈值
    14. w, h = int(det[2] * wT), int(det[3] * hT)
    15. x, y = int((det[0] * wT) - w / 2), int((det[1] * hT) - h / 2)
    16. #更新新的框
    17. bbox.append([x, y, w, h])
    18. #将索引加入到列表里面去
    19. classIds.append(classId)
    20. #置信度加入到创建的列表当中去
    21. confs.append(float(confidence))
    22. #对于这个函数,可以在目标检测中筛选置信度低于阈值的,还进行Nms筛选,
    23. # 至于参数,第一个参数是输入的预测框的尺寸,注意这里得尺寸是预测框左上角和右下角的尺寸,类似yolov3这种最后预测的坐标是中心点和长宽
    24. #第二个参数是预测中的的置信度得分
    25. #其实这个函数做了两件事情,对预测框和置信度设定阈值,进行筛选
    26. indices = cv.dnn.NMSBoxes(bbox, confs, 0.5, 0.6)
    27. #将框画出来
    28. for i in indices:
    29. i = i
    30. box = bbox[i]
    31. x, y, w, h = box[0], box[1], box[2], box[3]
    32. # print(x,y,w,h)
    33. cv.rectangle(img, (x, y), (x + w, y + h), (255, 0, 255), 2)
    34. cv.putText(img, f'{classNames[classIds[i]].upper()} {int(confs[i] * 100)}%',(x, y - 10), cv.FONT_HERSHEY_SIMPLEX, 0.6, (255, 0, 255), 2)

    将实现这一系列操作后,就可以完成整个物体检测的工作了,其中yolov3的模型的工作原理是:

     

    完整代码如下:(每一步都有注释方便大家更好的理解代码的含义)

    1. import numpy as np
    2. import cv2 as cv
    3. cap = cv.VideoCapture(0)
    4. ## 导入coco数据集,里面有各种80多种类别
    5. classesFile = "opencv_data_ku/coco.names"
    6. classNames = []
    7. with open(classesFile, 'rt') as f:
    8. classNames = f.read().rstrip('\n').split('\n')
    9. print(classNames)
    10. "===============引入我们的yolo3模块=========="
    11. ## Model Files
    12. #可以去yolo官网自己搜下面相关的文件参数
    13. modelConfiguration = "yolov3-320.cfg"
    14. modelWeights = "opencv_data_ku/yolov3.weights"
    15. #调用函数进行模型的配置和权重的配置
    16. net = cv.dnn.readNetFromDarknet(modelConfiguration, modelWeights)
    17. #要求网络使用其支持的特定计算后
    18. net.setPreferableBackend(cv.dnn.DNN_BACKEND_OPENCV)
    19. #使用CPU进行计算
    20. net.setPreferableTarget(cv.dnn.DNN_TARGET_CPU)
    21. def findObjects(outputs, img):
    22. hT, wT, cT = img.shape#输出照片的宽高通道数
    23. bbox = []
    24. classIds = []
    25. confs = []
    26. #对检测处的结果进行对比处理
    27. for output in outputs:
    28. for det in output:
    29. #可以看一下输出的内容
    30. scores = det[5:]#这里的意思是输出是某个种类的概率,前五个是框的位置以及置信度
    31. classId = np.argmax(scores)#找到是最大的种类编号
    32. confidence = scores[classId]#找到置信度
    33. if confidence > 0.5:#设立阈值
    34. w, h = int(det[2] * wT), int(det[3] * hT)
    35. x, y = int((det[0] * wT) - w / 2), int((det[1] * hT) - h / 2)
    36. #更新新的框
    37. bbox.append([x, y, w, h])
    38. #将索引加入到列表里面去
    39. classIds.append(classId)
    40. #置信度加入到创建的列表当中去
    41. confs.append(float(confidence))
    42. #对于这个函数,可以在目标检测中筛选置信度低于阈值的,还进行Nms筛选,
    43. # 至于参数,第一个参数是输入的预测框的尺寸,注意这里得尺寸是预测框左上角和右下角的尺寸,类似yolov3这种最后预测的坐标是中心点和长宽
    44. #第二个参数是预测中的的置信度得分
    45. #其实这个函数做了两件事情,对预测框和置信度设定阈值,进行筛选
    46. indices = cv.dnn.NMSBoxes(bbox, confs, 0.5, 0.6)
    47. #将框画出来
    48. for i in indices:
    49. i = i
    50. box = bbox[i]
    51. x, y, w, h = box[0], box[1], box[2], box[3]
    52. # print(x,y,w,h)
    53. cv.rectangle(img, (x, y), (x + w, y + h), (255, 0, 255), 2)
    54. cv.putText(img, f'{classNames[classIds[i]].upper()} {int(confs[i] * 100)}%',(x, y - 10), cv.FONT_HERSHEY_SIMPLEX, 0.6, (255, 0, 255), 2)
    55. while True:
    56. success, img = cap.read()
    57. #神经网络的输入图像需要采用称为blob的特定格式。用需要检测的原始图像image构造一个blob图像,
    58. #对原图像进行像素归一化1 / 255.0,缩放尺寸
    59. # (320, 320),交换了R与B通道
    60. blob = cv.dnn.blobFromImage(img, 1 / 255, (320, 320), 1, crop=False)
    61. #将blob变成网络的输入
    62. net.setInput(blob)
    63. #获取神经网络所有层的名称
    64. layersNames = net.getLayerNames()
    65. print('所有层:',layersNames)
    66. #我们不需要改变其他层,只需要找到未连接层,也就是网络的最后一层,在最后一层的基础上进行前向传播就可以了
    67. print('三个输出层的索引号',net.getUnconnectedOutLayers())
    68. for i in net.getUnconnectedOutLayers():
    69. outputNames = [layersNames[i - 1]]
    70. print('输出层名字',outputNames)
    71. #前向传播
    72. outputs = net.forward(outputNames)
    73. print(outputs)
    74. #了解每个输出层的形状
    75. print(outputs[0].shape)
    76. #调用框函数寻找对象的框
    77. findObjects(outputs, img)
    78. cv.imshow('Image', img)
    79. cv.waitKey(1)

  • 相关阅读:
    【C++入门】烦人的引用
    uniapp-从后台返回的一串地址信息上,提取省市区进行赋值
    218. 扑克牌 - 记忆化概率dp
    Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0
    【实验十二】决策树判断你是否可学python
    quasar q-layout布局说明
    使用python-docx完成word操作
    硕鼠——视频下载利器
    使用vscode编写第一个Hello World程序页面详细步骤
    2023年天津天狮学院专升本市场营销专业《管理学》考试大纲
  • 原文地址:https://blog.csdn.net/Superman980527/article/details/126905753