使用 Python 和 OpenCV 进行实时目标检测的详解

        使用到的模型文件我已经上传了,但是不知道能否通过审核,无法通过审核的话,就只能 靠大家自己发挥实力了,^_^

目录

简介

代码介绍

代码拆解讲解

1.首先,让我们导入需要用到的库:

2.然后,设置两个阈值:conf_threshold 和 nms_threshold,以及图片的宽度和高度:

3.接下来,我们加载预训练的 YOLOv3 模型,并加载识别的类名:

4.然后,我们创建一个颜色列表,以在最后的目标检测结果中为不同的类别绘制不同的颜色:

5.下一步,我们定义两个函数 fetch_frame 和 process_frame

6.在主循环中,我们使用 ThreadPoolExecutor 实现了并行处理读取帧和模型推理的操作。

7.退出程序

总体代码

效果展示

​编辑

使用GPU说明

如何操作

1.在读取模型时启用 CUDA:

2.其它代码不需要做太多修改:

总结        


简介

        这段程序是一个Python脚本,它使用了OpenCV库和预训练的YOLOv3模型来实现实时视频流的目标检测。它首先从摄像头捕获视频流,并使用线程池处理每一帧图像。在每一帧中,程序都会检测和识别不同的对象,并用不同的颜色显示结果。使用了非极大值抑制技术删除重复的检测框,并利用了并发处理技术以提高性能。最后,他还显示了每秒处理的帧数(FPS)。我们可以通过按'q'键来结束程序。这个程序展示了一种有效的使用深度学习模型进行实时视觉任务的方法。        


代码介绍

        这段代码是使用OpenCV和Python编写的,主要用于实时视频流处理和目标检测。代码的核心是使用训练好的YOLOv3模型来识别和定位视频帧中的对象。下面是对整个流程的详细解释:

  1. 导入库:首先,导入所需的库,包括cv2(OpenCV),numpy(用于科学计算),time(时间管理),以及concurrent.futures(用于并发执行)。

  2. 初始化参数:设置检测参数,包括置信度阈值(用于过滤掉那些置信度低于阈值的检测结果),非极大值抑制(NMS)阈值(用于去除重复的检测框),以及输入图像的宽和高。

  3. 加载模型和类别:通过使用cv2.dnn.readNet加载预训练的YOLOv3模型,并从coco.names文件中加载可以识别的类名列表。

  4. 颜色列表:为每个可识别的类别创建一个随机颜色列表,这用于在检测到的对象周围绘制彩色的边界框。

  5. 视频捕获:打开摄像头进行视频流的捕获。

  6. 多线程处理:初始化ThreadPoolExecutor以并发执行多个任务。这里定义了两个函数fetch_frameprocess_framefetch_frame用于从视频流中获取一帧图像,而process_frame则用于处理图像并执行目标检测。

  7. 目标检测流程

    • 转换输入帧:将输入帧转换为模型所需要的格式(blob),包括缩放、颜色空间转换等。
    • 执行检测:调用神经网络模型执行前向传播,获取检测结果。
    • 过滤结果:根据置信度阈值和NMS阈值过滤掉一部分检测结果,消除低置信度以及重复的检测框。
    • 绘制边界框和类别标签:在原图上绘制检测到的对象的边界框,并显示类别名称和置信度。
  8. 显示结果:在屏幕上实时显示处理后的视频帧,并计算显示FPS(每秒帧数)。

  9. 程序退出:等待我们按q键退出,并在退出前释放资源,包括摄像头和窗口。

        这段代码的设计利用了异步处理技术(通过ThreadPoolExecutor)来提高处理视频流的效率,使得帧的捕获和处理能够并行执行,从而尽可能提高FPS。

        为什么这样做,主要是我的电脑没有独立GPU,所以,呃,只能动点这中方法了,但是说实话并没有什么实质性的提升,难受了。


代码拆解讲解

        我们将使用预训练的 YOLOv3 模型进行目标检测,并使用 Python 的 concurrent.futures 库来并行处理视频帧的读取和模型推理,以优化程序的执行速度。

        感谢YOLO,虽然现在已经发展到v8了,但是我们这里使用v3还是足够了。

1.首先,让我们导入需要用到的库:

import cv2
import numpy as np
import time
from concurrent.futures import ThreadPoolExecutor

2.然后,设置两个阈值:conf_threshold 和 nms_threshold,以及图片的宽度和高度:

conf_threshold = 0.5
nms_threshold = 0.4
Width = 416
Height = 416

3.接下来,我们加载预训练的 YOLOv3 模型,并加载识别的类名:

net = cv2.dnn.readNet('../needFiles/yolov3.weights', '../needFiles/yolov3.cfg')
with open('../needFiles/coco.names', 'r') as f:
    classes = f.read().strip().split('\n')

4.然后,我们创建一个颜色列表,以在最后的目标检测结果中为不同的类别绘制不同的颜色:

color_list = np.random.uniform(0, 255, size=(len(classes), 3))

5.下一步,我们定义两个函数 fetch_frame 和 process_frame

        fetch_frame 用于从视频对象读取一帧;而 process_frame 则将读取到的帧输入到 YOLOv3 模型中,然后处理获得的输出结果,并在帧上绘制物体检测结果:

def fetch_frame(cap):
    ...
def process_frame(frame):
    ...

6.在主循环中,我们使用 ThreadPoolExecutor 实现了并行处理读取帧和模型推理的操作。

        这样可以使读取下一帧的操作和模型推理操作同时进行,从而显著地加快了处理速度:

executor = ThreadPoolExecutor(max_workers=2)
frame_future = executor.submit(fetch_frame, cap)

while True:
    ...
    ret, frame, height, width, channels = frame_future.result()
    frame_future = executor.submit(fetch_frame, cap)
    processed_frame = executor.submit(process_frame, frame).result()
    ...

7.退出程序

        在这段代码的最后,如果你按下 q 键退出主循环,视频读取对象将会被释放,所有的窗口也将被销毁:

if cv2.waitKey(1) & 0xFF == ord('q'):
    break
...
cap.release()
cv2.destroyAllWindows()

总体代码

# 导入必要的库
import cv2
import numpy as np
import time
from concurrent.futures import ThreadPoolExecutor

# 设置置信度阈值和非极大值抑制(NMS)阈值
conf_threshold = 0.5
nms_threshold = 0.4
Width = 416
Height = 416

# 加载预训练的 YOLOv3 模型
net = cv2.dnn.readNet('../needFiles/yolov3.weights', '../needFiles/yolov3.cfg')

# 加载可识别的类名
with open('../needFiles/coco.names', 'r') as f:
    classes = f.read().strip().split('\n')

# 为不同的类别创建一个颜色列表
color_list = np.random.uniform(0, 255, size=(len(classes), 3))

# 打开摄像头进行视频帧的捕获
cap = cv2.VideoCapture(0)

# 初始化一个ThreadPoolExecutor用于多线程处理
executor = ThreadPoolExecutor(max_workers=2)


# 定义fetch_frame函数,从视频流中获取视频帧
def fetch_frame(cap):
    ret, frame = cap.read()  # 读取一帧图像
    height, width, channels = frame.shape  # 获取图像的尺寸和通道信息
    return ret, frame, height, width, channels


# 定义process_frame函数,处理每帧图像并进行目标检测
def process_frame(frame):
    # 将帧转换为模型的输入格式
    blob = cv2.dnn.blobFromImage(frame, 1 / 255.0, (416, 416), swapRB=True, crop=False)
    net.setInput(blob)
    output_layers = net.getUnconnectedOutLayersNames()  # 获取输出层的名字
    layer_outputs = net.forward(output_layers)  # 进行前向传播,获取检测结果

    boxes = []  # 用于存储检测到的边界框
    confidences = []  # 用于存储边界框的置信度
    class_ids = []  # 用于存储边界框的类别ID

    # 循环每个输出层的检测结果
    for output in layer_outputs:
        for detection in output:
            scores = detection[5:]  # 获取类别的得分
            class_id = np.argmax(scores)  # 获取得分最高的类别ID
            confidence = scores[class_id]  # 获取得分最高的置信度

            # 过滤低置信度的检测结果
            if confidence > conf_threshold:
                center_x = int(detection[0] * width)
                center_y = int(detection[1] * height)
                w = int(detection[2] * width)
                h = int(detection[3] * height)

                # 计算边界框的位置和尺寸
                x = int(center_x - w / 2)
                y = int(center_y - h / 2)

                # 将边界框的位置、尺寸、置信度和类别ID添加到列表中
                boxes.append([x, y, w, h])
                confidences.append(float(confidence))
                class_ids.append(class_id)

    # 使用非极大值抑制去除重叠的边界框
    indices = cv2.dnn.NMSBoxes(boxes, confidences, conf_threshold, nms_threshold)

    # 在原图上绘制边界框和类别标签
    for i in indices.flatten():
        box = boxes[i]
        x = box[0]
        y = box[1]
        w = box[2]
        h = box[3]
        label = str(classes[class_ids[i]])
        color = color_list[class_ids[i]]
        cv2.rectangle(frame, (x, y), (x + w, y + h), color, 2)
        cv2.putText(frame, label, (x, y - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, color, 2)

    return frame


# 在进入循环前,先读取一帧以开始异步处理
frame_future = executor.submit(fetch_frame, cap)

# 主循环
while True:
    start = time.time()  # 记录开始处理的时间点
    # 获取当前帧和相应信息
    ret, frame, height, width, channels = frame_future.result()

    # 异步读取下一帧
    frame_future = executor.submit(fetch_frame, cap)

    # 如果当前帧读取成功,则继续处理
    if ret:
        # 使用线程池异步处理当前帧
        processed_frame = executor.submit(process_frame, frame).result()

        # 计算FPS
        end = time.time()
        fps = 1 / (end - start)
        # 在处理好的帧上显示FPS
        cv2.putText(processed_frame, "FPS: " + str(round(fps, 2)), (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0),
                    2)
        # 显示处理好的帧
        cv2.imshow('frame', processed_frame)

        # 如果我们按下 ‘q’ 键,退出循环
        if cv2.waitKey(1) & 0xFF == ord('q'):
            break
    else:
        break  # 如果帧没有被成功读取,退出循环

# 释放视频捕获对象和销毁所有OpenCV窗口
cap.release()
cv2.destroyAllWindows()

效果展示

        这个效果还是不错的哈,就是这个硬件性能跟不上,要是有独显就好了。 


使用GPU说明

        当然了,为了在大学时期狠狠的奖励自己四年游戏,很多同学应该都是购买的游戏本,那么恭喜你,你的硬件非常的完美,那么这里你可以看一下。

如何操作

        如果你想利用 GPU 加速你的目标检测代码,主要更改会集中在如何让 OpenCV 利用你的 GPU。不幸的是,OpenCV 的 dnn 模块默认使用 CPU 进行计算。想要使用 GPU,首要条件是你需要有一个支持 CUDA 的 NVIDIA GPU。

        从 OpenCV 4.2 版本开始,dnn 模块添加了对 CUDA 的支持,但实现这一点需要确保你的 OpenCV 是用 CUDA 支持构建的。如果你自己编译 OpenCV,确保在编译时启用了 CUDA 支持。

以下是如何更改你的代码以利用 CUDA 的基本步骤:

1.在读取模型时启用 CUDA:

替换代码中的 readNet 调用,使用 readNetFromDarknet 并添加代码来设置网络的首选后端和目标为 CUDA。

   # 加载预训练的 YOLOv3 模型
   net = cv2.dnn.readNetFromDarknet('../needFiles/yolov3.cfg', '../needFiles/yolov3.weights')
   
   # 启用 CUDA
   net.setPreferableBackend(cv2.dnn.DNN_BACKEND_CUDA)
   net.setPreferableTarget(cv2.dnn.DNN_TARGET_CUDA)
2.其它代码不需要做太多修改:

        使用CUDA优化后,主要是模型推理过程(即net.forward()的调用)会更快。数据准备和后处理(例如非极大值抑制)的代码不需要太多修改,因为它们仍然在 CPU 上执行。

        这些改动仅在你已有 OpenCV 版本支持 CUDA,并且你的系统拥有合适的 NVIDIA GPU 时有效。如果你的环境满足这些条件,这样的更改可以显著加快模型推理的速度,尤其是在进行视频流处理时。

        最后,你一定要确定你的环境(包括 NVIDIA 驱动程序和 CUDA Toolkit)被正确设置以支持 GPU 加速。如果你对如何编译支持 CUDA 的 OpenCV 或如何配置你的系统环境有任何疑问,我建议查阅 OpenCV 官方文档和 NVIDIA 的 CUDA 安装指导,因为我真的没有仔细研究过。

总结        

        希望这篇博客对你有所帮助。最后我想说,虽然NVIDIA对我们的学习有帮助,但是我还是想说。

AMDyes!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/587614.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

《QT实用小工具·四十三》历史编辑器(支持历史搜索 关键字匹配)

1、概述 源码放在文章末尾 该项目实现了在输入框中输入部分信息能全部展现之前的历史输入信息&#xff0c;支持历史搜索和关键词匹配&#xff0c;项目demo演示如下所示&#xff1a; 项目部分代码如下所示&#xff1a; #include "historymodel.h" #include <QM…

Java发送请求-http+https的

第一步&#xff1a;建议ssl连接对象&#xff0c;信任所有证书 第二步&#xff1a;代码同时支持httphttps 引入源码类 是一个注册器 引入这个类&#xff0c;和它的方法create 注册器&#xff0c;所以对http和https都进行注册&#xff0c;参数为id和item&#xff0c;其中http的…

【已解决】pandas读excel中长数字变成科学计数法的问题

pandas 读excel中的长数字时&#xff0c;即使excel中已经设置为文本&#xff0c;读进df后也会自动变成科学计数法。 在日常的数据分析和处理工作中&#xff0c;Excel和pandas是数据分析师们不可或缺的得力助手。然而&#xff0c;在使用pandas读取Excel文件时&#xff0c;我们有…

CSAPP | Floating Point

CSAPP | Floating Point b i b_i bi​ b i − 1 b_{i-1} bi−1​ … b 2 b_2 b2​ b 1 b_1 b1​ b 0 b_0 b0​ b − 1 b_{-1} b−1​ b − 2 b_{-2} b−2​ b − 3 b_{-3} b−3​ … b − j b_{-j} b−j​ S ∑ k − j i b k 2 k S\sum_{k-j}^{i}b_k\times2^k S∑k…

如何批量复制多个文件到多个目录中(批量复制文件,多对多文件高效操作的方法)

首先&#xff0c;需要用到的这个工具&#xff1a; 度娘网盘 提取码&#xff1a;qwu2 蓝奏云 提取码&#xff1a;2r1z 现在开始说具体操作 1、首先&#xff0c;我准备了3个文件夹和两个可爱的图片&#xff1a; 当然&#xff0c;在实际使用的时候肯定不止这些&#xff0c;我这…

升级 Vite 5 出现警告 The CJS build of Vite‘s Node API is deprecated

错误描述 vue3-element-admin 项目将Vite4 升级至 Vite5 后,项目运行出现如下警告: The CJS build of Vites Node API is deprecated. See https://vitejs.dev/guide/troubleshooting.html#vite-cjs-node-api-deprecated for more details.图片 问题原因 Vite 官方弃用 C…

怎么用微信小程序实现远程控制台球室

怎么用微信小程序实现远程控制台球室呢&#xff1f; 本文描述了使用微信小程序调用HTTP接口&#xff0c;实现控制台球室&#xff0c;控制球台上方的照明灯&#xff0c;单台设备可控制多张球台的照明灯。 可选用产品&#xff1a;可根据实际场景需求&#xff0c;选择对应的规格 …

【源码解析】深入Pandas的心脏DataFrame 含十大功能、源码实现与编程知识点

作者介绍&#xff1a;10年大厂数据\经营分析经验&#xff0c;现任大厂数据部门负责人。 会一些的技术&#xff1a;数据分析、算法、SQL、大数据相关、python 欢迎加入社区&#xff1a;码上找工作 作者专栏每日更新&#xff1a; LeetCode解锁1000题: 打怪升级之旅 python数据分析…

Arcpy开发记录

一.GDB数据库相关 1.单独的shape更新时&#xff0c;不会有限制&#xff0c;数据会自动截取 2.在GDB下&#xff0c;使用UpdateCursor更新字段时&#xff0c;填入的数据长度必须与字段长度要求一致&#xff0c;否则报错&#xff1a; 二.arcpy.da.UpdateCursor相关 updateRow后关…

【无线通信开发应用】nRF905数据手册深度解读

希望通过两个stm32、两个nRF905无线通信模块、串口来实现两机通信。具体功能为&#xff1a; 板子A、B分别包含一个stm32单片机和一个nRF905无线模块&#xff0c;欲实现板子A、B之间的通信。 其中&#xff0c;PC端串口助手可向板子A的stm32发送字符‘A’控制板子B上的LED亮灯&am…

Linux migrate_type初步探索

1、基础知识 我们都知道Linux内存组织管理结构架构&#xff0c;顶层是struct pglist_data&#xff0c;然后再到struct zone&#xff0c;最后是struct page。大概的管理结构是这样的&#xff1a; 根据物理内存的地址范围可划分不同的zone&#xff0c;每个zone里的内存由buddy…

【redis】Redis数据类型(三)List类型

目录 List类型介绍特点 List数据结构附&#xff1a;3.2以前的版本(介绍一下压缩列表和双向链表)压缩列表ZipList双向链表LinkedList 常用命令lpush示例 lpushx示例 rpush示例 rpushx示例 LPOP示例 RPOP示例 BLPOP非阻塞行为阻塞行为相同的 key 被多个客户端同时阻塞在 MULTI/EX…

【类型商店】字符字符串(上)

啊&#xff0c;哈喽&#xff0c;小伙伴们大家好。我是#Y清墨&#xff0c;今天呐&#xff0c;我要介绍的是字符与字符串。 导语 在我之前发的[算法材料包]C数字拆分里有提及到.size()和length()和strlen(),想看更多内容请别划走&#xff01; 一.论字符 &#xff08;1&#xff…

口袋实验室--使用AD2高效调试IIC、UART、SPI等低速接口

目录 1. 简介 2. 调试过程 2.1 简要步骤 2.2 Si5338 寄存器配置流程 2.3 AD2的基本配置 2.4 检查Si5338状态 2.5 配置Si5338寄存器 2.6 保存Si5338寄存器 3. 总结 1. 简介 使用Digilent Analog Discovery 2进行调试不仅提升了工作效率&#xff0c;而且极大地简化了常…

Golang | Leetcode Golang题解之第60题排列序列

题目&#xff1a; 题解&#xff1a; func getPermutation(n int, k int) string {factorial : make([]int, n)factorial[0] 1for i : 1; i < n; i {factorial[i] factorial[i - 1] * i}k--ans : ""valid : make([]int, n 1)for i : 0; i < len(valid); i {…

2024年第二十一届 五一杯 (C题)大学生数学建模挑战赛|数学建模完整代码+建模过程全解全析

当大家面临着复杂的数学建模问题时&#xff0c;你是否曾经感到茫然无措&#xff1f;作为2022年美国大学生数学建模比赛的O奖得主&#xff0c;我为大家提供了一套优秀的解题思路&#xff0c;让你轻松应对各种难题。 CS团队倾注了大量时间和心血&#xff0c;深入挖掘解决方案。通…

pyinstaller打包pytorch和transformers程序

记录使用pyinstaller打包含有pytorch和transformers库的程序时遇到的问题和解决方法。 环境和版本信息 操作系统&#xff1a;Windows 11 Python&#xff1a;3.10.12 pyinstaller&#xff1a;5.13.0 torch&#xff1a;2.2.2 transformers&#xff1a;4.40.1 打包过程和问…

【进收藏夹吃灰系列】算法学习指南

文章目录 [toc]分治算法 个人主页&#xff1a;丷从心 系列专栏&#xff1a;进收藏夹吃灰系列 分治算法 博客标题博客url【分治算法】【Python实现】Hanoi塔问题https://blog.csdn.net/from__2024_04_11/article/details/138093461?spm1001.2014.3001.5502

Mysql-黑马

Mysql-黑马 编写规范&#xff1a;## 一级1. 二级三级 1.Mysql概述 数据库概念mysql数据仓库 cmd启动和停止 net start mysql180 net stop mysql180备注&#xff1a;其中的mysql180是服务名 客户端连接 远程连接数据仓库 -h 主机号 -P端口号 mysql [-h 127.0.0.1] [-P 33…

CSDN如何在个人主页开启自定义模块|微信公众号

目前只有下面三种身份才具有这个功能。 VIP博客专家企业博客 栏目内容不知道怎么写HTML的&#xff0c;可以联系我帮你添加