OpenCV图像处理——Python开发中OpenCV视频流的多线程处理方式

前言

在做视觉类项目中,常常需要在Python环境下使用OpenCV读取本地的还是网络摄像头的视频流,之后再调入各种模型,如目标分类、目标检测,人脸识别等等。如果使用单线程处理,很多时候会出现比较严重的时延,如果算力吃紧,模型推理所占用的更长的话,这种延迟感会更加明显,会出现卡帧的现象。在这种情况下,往往要把代码从单线程改为了多线程,即单独用一个线程实时捕获视频帧,主线程在需要时从子线程拷贝最近的帧使用即可。
单线程处理视频流时,如果目标检测模型较大或者任务复杂,会影响处理速度。而使用多线程,让视频捕获和目标检测分别在各自的线程中运行,能够更充分地利用 CPU 的多核心处理能力,提高整体的处理效率和实时性。
在实时视频处理中,特别是涉及到深度学习模型推理这种计算密集型的任务时,多线程确实能够带来显著的性能提升。通过将视频捕获和处理分开,可以避免由于处理时间过长而导致的帧丢失或延迟。

一、多线程

在 Python 中,可以使用 threading 模块来实现多线程。下面是一个简单的例子,演示了如何在 Python 中创建和使用多线程:

1、导入 threading 模块

首先导入 Python 的 threading 模块,它提供了多线程编程所需的功能。

import threading

2、创建线程执行函数

定义一个函数,作为线程的执行体。这个函数将会在每个线程中运行。在函数内编写希望线程执行的代码逻辑。

def my_function():# Your code herepass

3、创建线程对象

使用 threading.Thread() 创建一个线程对象,将目标函数指定为刚才定义的函数,并传入所需参数。

my_thread = threading.Thread(target=my_function, args=(arg1, arg2)) # 传入参数 args

4、动线程

使用 start() 方法启动线程。

my_thread.start()

5、等待线程执行完成

使用 join() 方法等待线程执行完毕。这会让主线程等待子线程的结束。

my_thread.join()

6、示例

以下是一个简单的示例,演示了如何使用多线程:

import threading
import time# 线程执行体函数
def print_numbers():for i in range(5):print(f"Child Thread: {i}")time.sleep(1)# 创建线程对象
thread = threading.Thread(target=print_numbers)# 启动线程
thread.start()# 主线程中的其他操作
for i in range(5):print(f"Main Thread: {i}")time.sleep(0.5)# 等待子线程执行结束
thread.join()print("Main Thread exiting...")

print_numbers() 函数是子线程的执行体,在子线程中打印数字。主线程在启动子线程后,会同时进行自己的任务。最后通过 join() 方法等待子线程结束。

二、视频处理

一般视频处理代码分为两部分:从相机读取下一个可用帧以及对帧进行图像处理,例如把图像送到Yolov5目标检测模型进行检测。
在没有多线程的程序中,按顺序读取下一帧并进行处理。程序等待下一帧可用,然后对其进行必要的处理。读取帧所需的时间主要取决于请求、等待下一个视频帧,并将其从相机传输到内存的时间。无论是在 CPU 还是 GPU 上,对视频帧进行计算所需的时间都占据了视频处理所花费的大部分时间。

然而,在具有多线程的程序中,读取下一帧并对其进行处理不需要按顺序进行。当一个线程执行读取下一帧的任务时,主线程可以使用 CPU 或 GPU 来处理最后一个读取的帧。通过这种方式,这两个任务可以重叠执行,从而减少读取和处理帧的总时间。

1.视频单线程处理

# importing required libraries 
import cv2 
import time# opening video capture stream
vcap = cv2.VideoCapture(0)
if vcap.isOpened() is False :print("[Exiting]: Error accessing webcam stream.")exit(0)
fps_input_stream = int(vcap.get(5))
print("FPS of webcam hardware/input stream: {}".format(fps_input_stream))
grabbed, frame = vcap.read() # reading single frame for initialization/ hardware warm-up# processing frames in input stream
num_frames_processed = 0 
start = time.time()
while True :grabbed, frame = vcap.read()if grabbed is False :print('[Exiting] No more frames to read')break# adding a delay for simulating time taken for processing a frame delay = 0.03 # delay value in seconds. so, delay=1 is equivalent to 1 second time.sleep(delay) num_frames_processed += 1cv2.imshow('frame' , frame)key = cv2.waitKey(1)if key == ord('q'):break
end = time.time()# printing time elapsed and fps 
elapsed = end-start
fps = num_frames_processed/elapsed 
print("FPS: {} , Elapsed Time: {} , Frames Processed: {}".format(fps, elapsed, num_frames_processed))# releasing input stream , closing all windows 
vcap.release()
cv2.destroyAllWindows()

2.视频多线程处理

# importing required libraries 
import cv2 
import time 
from threading import Thread # library for implementing multi-threaded processing# defining a helper class for implementing multi-threaded processing 
class WebcamStream :def __init__(self, stream_id=0):self.stream_id = stream_id   # default is 0 for primary camera # opening video capture stream self.vcap      = cv2.VideoCapture(self.stream_id)if self.vcap.isOpened() is False :print("[Exiting]: Error accessing webcam stream.")exit(0)fps_input_stream = int(self.vcap.get(5))print("FPS of webcam hardware/input stream: {}".format(fps_input_stream))# reading a single frame from vcap stream for initializing self.grabbed , self.frame = self.vcap.read()if self.grabbed is False :print('[Exiting] No more frames to read')exit(0)# self.stopped is set to False when frames are being read from self.vcap stream self.stopped = True# reference to the thread for reading next available frame from input stream self.t = Thread(target=self.update, args=())self.t.daemon = True # daemon threads keep running in the background while the program is executing # method for starting the thread for grabbing next available frame in input stream def start(self):self.stopped = Falseself.t.start()# method for reading next frame def update(self):while True :if self.stopped is True :breakself.grabbed , self.frame = self.vcap.read()if self.grabbed is False :print('[Exiting] No more frames to read')self.stopped = Truebreak self.vcap.release()# method for returning latest read frame def read(self):return self.frame# method called to stop reading frames def stop(self):self.stopped = True# initializing and starting multi-threaded webcam capture input stream 
webcam_stream = WebcamStream(stream_id=0) #  stream_id = 0 is for primary camera 
webcam_stream.start()# processing frames in input stream
num_frames_processed = 0 
start = time.time()
while True :if webcam_stream.stopped is True :breakelse :frame = webcam_stream.read()# adding a delay for simulating time taken for processing a frame delay = 0.03 # delay value in seconds. so, delay=1 is equivalent to 1 second time.sleep(delay) num_frames_processed += 1cv2.imshow('frame' , frame)key = cv2.waitKey(1)if key == ord('q'):break
end = time.time()
webcam_stream.stop() # stop the webcam stream# printing time elapsed and fps 
elapsed = end-start
fps = num_frames_processed/elapsed 
print("FPS: {} , Elapsed Time: {} , Frames Processed: {}".format(fps, elapsed, num_frames_processed))# closing all windows 
cv2.destroyAllWindows()

上面的代码创建了一个 WebcamStream 类,其中包含了多线程读取相机帧的逻辑。在主循环中,它仍然以顺序方式处理每个帧,但是读取帧的线程是在后台运行的。

但上面的代码提升速度的同时,还有以下有改进的问题:

  • 处理多个帧的逻辑: 代码在主循环中每次处理帧时都有一个固定的延迟 time.sleep(delay),这并不真实地模拟出帧处理的时间。应该考虑记录每个帧的时间戳,并在处理完帧后计算帧处理的实际时间。

  • 多线程下的帧处理: 虽然视频流读取部分在单独的线程中,但是主循环仍然是顺序执行的,它等待每个帧进行处理。在多线程环境中,也许值得考虑在单独的线程中对帧进行处理。

  • 内存和资源管理: 确保在程序退出时释放所有资源,特别是在多线程环境中,需要小心确保线程的安全退出。

  • 代码结构和注释: 为了更好地可读性和维护性,添加一些注释来解释每个函数和方法的作用,以及代码块的意图。

3.多线程代码优化

  • 去除固定延迟的处理方式: 代码在处理每一帧时都有固定的延迟。考虑使用实际帧处理时间的方法,而不是使用固定的延迟。这可以通过记录每个帧的时间戳来实现。

  • 并行处理视频帧: 在主线程中按顺序处理每一帧。在多线程环境下,可以考虑使用多个线程并行处理视频帧,以加快处理速度。

  • 资源释放: 在程序结束时,确保释放所有资源。这包括在适当的时候关闭视频流、终止线程等。

import cv2 
import time 
from threading import Threadclass WebcamStream:def __init__(self, stream_id=0):self.stream_id = stream_idself.vcap = cv2.VideoCapture(self.stream_id)if not self.vcap.isOpened():print("[Exiting]: Error accessing webcam stream.")exit(0)self.fps_input_stream = int(self.vcap.get(cv2.CAP_PROP_FPS))print("FPS of webcam hardware/input stream: {}".format(self.fps_input_stream))self.grabbed, self.frame = self.vcap.read()if not self.grabbed:print('[Exiting] No more frames to read')exit(0)self.stopped = Falseself.t = Thread(target=self.update, args=())self.t.daemon = Trueself.t.start()def update(self):while not self.stopped:grabbed, frame = self.vcap.read()if not grabbed:print('[Exiting] No more frames to read')self.stopped = Truebreakself.frame = framedef read(self):return self.framedef stop(self):self.stopped = Trueself.t.join()self.vcap.release()webcam_stream = WebcamStream(stream_id=0)
num_frames_processed = 0
start = time.time()
while True:frame = webcam_stream.read()if webcam_stream.stopped:breakdelay = 0.03time.sleep(delay)num_frames_processed += 1cv2.imshow('frame', frame)key = cv2.waitKey(1)if key == ord('q'):break
end = time.time()
webcam_stream.stop()
elapsed = end - start
fps = num_frames_processed / elapsed
print("FPS: {} , Elapsed Time: {} , Frames Processed: {}".format(fps, elapsed, num_frames_processed))
cv2.destroyAllWindows()

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

H3C ER G2系列路由器信息泄露漏洞

H3C ER G2系列路由器信息泄露漏洞 免责声明漏洞描述漏洞影响漏洞危害漏洞页面漏洞复现1. 构造poc2. 发生数据包,获取密码3. 登录系统 免责声明 仅用于技术交流,目的是向相关安全人员展示漏洞利用方式,以便更好地提高网络安全意识和技术水平。 任何人不得利用该文章…

mac电脑html文件 局域网访问

windows html文件 局域网访问 参考 https://blog.csdn.net/qq_38935512/article/details/103271291mac电脑html文件 局域网访问 开发工具vscode 安装vscode插件 Live Server 完成后打开项目的html 右键使用Live Server打开页面 效果如下,使用本地ip替换http://12…

利用高级 CSPM 应对现代攻击

混合云和多云环境的快速增长造成了跨架构的复杂性,使得人们很难清楚、完整地了解技术堆栈中的各种平台。最近云攻击和破坏的激增引起了人们对团队应如何有效地保护和运行云中的应用程序的关注。 因为错误配置是云环境中安全威胁的首位,并且是基于云的攻…

cfa一级考生复习经验分享系列(四)

备考CFA一级满打满算用了一个多月,每天八个小时以上。可能如果仅以通过为目标的话完全不用这样,看过太多类似于只看了一周就通过了考试又或是放弃了好几门飘过了考试的情况,我觉得这是不正确的考试状态,完全不必惊叹,踏…

学生护眼灯最好是哪一款?考研受欢迎的护眼台灯推荐

当前我国近视防控面临严峻局面,数据显示,我国近视发病率比较高,2012年在我国5岁以上总人口中,近视的人数在4.5亿人左右:到2050年,全世界每十个人中就有一个人患有近视,而这其中1/3将会是中国人。世界卫生组…

【Qt QML入门】Button

Button表示一个推按钮控件,用户可以按下或单击它。 import QtQuick import QtQuick.Window import QtQuick.ControlsWindow {id: winwidth: 800height: 600visible: truetitle: qsTr("Hello World")Button {id: btnwidth: 200height: 100anchors.centerIn…

10-索引-索引二分-H 指数 II

这是索引二分的第十篇算法,力扣链接 给你一个整数数组 citations ,其中 citations[i] 表示研究者的第 i 篇论文被引用的次数,citations 已经按照 升序排列 。计算并返回该研究者的 h 指数。 h 指数的定义:h 代表“高引用次数”&am…

MIT_线性代数笔记:第 17 讲 正交矩阵和施密特正交化

目录 正交向量 Orthonormal vectors标准正交矩阵 Orthonormal matrix标准正交列向量的优势 Orthonormal columns are good施密特正交化 Gram-Schmidt 本讲我们完成对“正交”的介绍。Gram-Schmidt 过程可以将原空间的一组基转变为标准正交基。 正交向量 Orthonormal vectors 满…

VHDL实验:基于有限状态机实现秒表

题目要求: 利用有限状态机实现实现一个具有启动、停止、清零功能的秒表,显示格式:分:秒:十分秒。启动、停止、清零由一个按键控制,按键按下时,功能按启动、停止、清零顺序循环。 思路分析&…

vue和jQuery有什么区别

Vue 和 jQuery 是两种不同类型的前端工具,它们有一些显著的区别: Vue 响应式数据绑定:Vue 提供了双向数据绑定和响应式更新的能力,使得数据与视图之间的关系更加直观和易于维护。组件化开发:Vue 鼓励使用组件化的方式…

代码随想录27期|Python|Day15|二叉树|层序遍历|对称二叉树|翻转二叉树

本文图片来源:代码随想录 层序遍历(图论中的广度优先遍历) 这一部分有10道题,全部可以套用相同的层序遍历方法,但是需要在每一层进行处理或者修改。 102. 二叉树的层序遍历 - 力扣(LeetCode) 层…

【深度学习】强化学习(五)深度强化学习

文章目录 一、强化学习问题1、交互的对象2、强化学习的基本要素3、策略(Policy)4、马尔可夫决策过程5、强化学习的目标函数6、值函数7、深度强化学习1. 背景与动机2. 关键要素3. 成功案例4. 挑战和未来展望5. 核心概念和方法总结 一、强化学习问题 强化学…

关于react native项目中使用react-native-wechat-lib@3.0.4

关于react native项目中使用react-native-wechat-lib3.0.4 插件官网安装依赖包(Android和iOS下载插件完成后记得更新依赖,)Android中配置1.在项目文件夹下面创建文件夹wxapi(如上图)2.在文件MainApplication.java中如下…

基于Java SSM框架实现高校人事管理系统项目【项目源码】计算机毕业设计

基于java的SSM框架实现高校人事管理系统演示 JSP技术介绍 JSP技术本身是一种脚本语言,但它的功能是十分强大的,因为它可以使用所有的JAVA类。当它与JavaBeans 类进行结合时,它可以使显示逻辑和内容分开,这就极大的方便了用户的需…

树莓派,opencv,Picamera2利用舵机云台追踪人脸

一、需要准备的硬件 Raspiberry 4b两个SG90 180度舵机(注意舵机的角度,最好是180度且带限位的,切勿选360度舵机)二自由度舵机云台(如下图)Raspiberry CSI 摄像头 组装后的效果: 二、项目目标…

【漏洞复现】华脉智联指挥调度平台/script_edit/fileread.php文件读取漏洞

Nx01 产品简介 深圳市华脉智联科技有限公司,融合通信系统将公网集群系统、专网宽带集群系统、不同制式、不同频段的短波/超短波对讲、模拟/数字集群系统、办公电话系统、广播系统、集群单兵视频、视频监控系统、视频会议系统等融为一体,集成了专业的有线…

Axure->Axure安装,Axure菜单栏和工具栏功能介绍,页面及概要区

目录 一.项目的全周期 二.产品经理的介绍 三.Axure安装 四.Axure的基本使用 1.Axure菜单栏和工具栏功能介绍,页面及概要区 一.项目的全周期 二.产品经理的介绍 同时做五个项目,每个项目100w,一个项目成本需要50-60w,那么五个…

如何进行图形界面和图形编程?

图形界面(GUI)和图形编程是计算机科学领域中的重要方向,它们使得软件更加直观、易用。C语言虽然是一种面向过程的编程语言,不像一些现代语言有直接的图形界面支持,但我们仍然可以通过使用图形库来实现基本的图形编程。…

做数据分析为何要学统计学(10)——什么是回归分析

​回归分析(regression analysis)是量化两种或两种以上因素/变量间相互依赖关系的统计分析方法。回归分析根据因素的数量,分为一元回归和多元回归分析;按因素之间依赖关系的复杂程度,可分为线性回归分析和非线性回归分析。我们通过…

机器学习算法应用场景与评价指标

一、应用场景 机器学习的算法选择大部分依赖于具体的问题类型和数据特征。下面是一些典型的场景以及对应的常用算法: 1.二元分类问题 当你的目标变量只有两个类别时,如垃圾邮件过滤(垃圾邮件/非垃圾邮件)、患者疾病诊断&#x…