当前位置: 首页 > news >正文

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()

相关文章:

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

前言 在做视觉类项目中,常常需要在Python环境下使用OpenCV读取本地的还是网络摄像头的视频流,之后再调入各种模型,如目标分类、目标检测,人脸识别等等。如果使用单线程处理,很多时候会出现比较严重的时延,…...

webGL开发智慧城市流程

开发智慧城市的WebGL应用程序涉及多个方面,包括城市模型、实时数据集成、用户界面设计等。以下是一个一般性的流程,您可以根据项目的具体需求进行调整,希望对大家有所帮助。 1.需求分析: 确定智慧城市应用程序的具体需求和功能。考…...

Django讲课笔记02:Django环境搭建

文章目录 一、学习目标二、相关概念(一)Python(二)Django 三、环境搭建(一)安装Python1. 从官方网站下载最新版本的Python2. 运行安装程序并按照安装向导进行操作3. 勾选添加到路径复选框4. 完成安装过程5.…...

黑豹程序员-原生JS拖动div到任何地方-自定义布局

效果图 代码html <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <html xmlns"http://www.w3.org/1999/xhtml"> <head> <meta http-equiv"Content-Type" content"text/html; charsetutf-8" /…...

<软考高项备考>《论文专题 - 7 论文的项目背景之技术架构》

1 技术架构概况 ➢ 架构前端:HTML ➢ 后端:Java ➢ 数据库: Oracle ➢ 大数据:MapReduce ➢ 人工智能:Python ➢ 物联网:RFID识别&#xff0c;http传输&#xff0c;Java ➢ 开发APP: IOS、Android 2 常用开发语言 序号语言说明1JavaJava是一种跨平台的编程语言&#xff0c;广…...

6.3 C++11 原子操作与原子类型

一、原子类型 1.多线程下的问题 在C中&#xff0c;一个全局数据在多个线程中被同时使用时&#xff0c;如果不加任何处理&#xff0c;则会出现数据同步的问题。 #include <iostream> #include <thread> #include <chrono> long val 0;void test() {for (i…...

智能优化算法应用:基于狮群算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于狮群算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于狮群算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.狮群算法4.实验参数设定5.算法结果6.参考文献7.MA…...

BERT、GPT学习问题个人记录

目录 1. 为什么过去几年大家都在做BERT, 做GPT的人少。 2. 但最近做GPT的多了以及为什么GPT架构的scaling&#xff08;扩展性&#xff09;比BERT好。 3.BERT是否可以用来做生成&#xff0c;如果可以的话为什么大家都用GPT不用BERT. 4. BERT里的NSP后面被认为是没用的&#x…...

HeartBeat监控Mysql状态

目录 一、概述 二、 安装部署 三、配置 四、启动服务 五、查看数据 一、概述 使用heartbeat可以实现在kibana界面对 Mysql 服务存活状态进行观察&#xff0c;如有必要&#xff0c;也可在服务宕机后立即向相关人员发送邮件通知 二、 安装部署 参照章节&#xff1a;监控组件…...

软件开发经常出现的bug原因有哪些

软件开发中出现bug的原因是多方面的&#xff0c;这些原因可能涉及到开发流程、人为因素、设计问题以及其他一系列因素。以下是一些常见的导致bug的原因&#xff1a; 1. 错误的需求分析&#xff1a; 不正确、不完整或者模糊的需求分析可能导致开发人员误解客户的需求&#xff0…...

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

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

鸿蒙开发组件之Web

一、加载一个url myWebController: WebviewController new webview.WebviewControllerbuild() {Column() {Web({src: https://www.baidu.com,controller: this.myWebController})}.width(100%).height(100%)} 二、注意点 2.1 不能用Previewer预览 Web这个组件不能使用预览…...

成绩分析。

成绩分析 题目描述 小蓝给学生们组织了一场考试&#xff0c;卷面总分为 100分&#xff0c;每个学生的得分都是一个0到100的整数。 请计算这次考试的最高分、最低分和平均分 输入描述 输入的第一行包含一个整数n(1n104)&#xff0c;表示考试人数。 接下来n行&#xff0c;每行包含…...

Excel实现字母+数字拖拉自动递增,步长可更改

目录 1、带有字母的数字序列自增加&#xff08;步长可变&#xff09; 2、仅字母自增加 3、字母数字同时自增 1、带有字母的数字序列自增加&#xff08;步长可变&#xff09; 使用Excel通常可以直接通过拖拉的方式&#xff0c;实现自增数字&#xf…...

Java之Stream流

一、什么是Stream流 Stream是一种处理集合&#xff08;Collection&#xff09;数据的方式。Stream可以让我们以一种更简洁的方式对集合进行过滤、映射、排序等操作。 二、Stream流的使用步骤 先得到一条Stream流&#xff0c;并把数据放上去利用Stream流中的API进行各种操作 中间…...

vue中element-ui日期选择组件el-date-picker 清空所选时间,会将model绑定的值设置为null 问题 及 限制起止日期范围

一、问题 在Vue中使用Element UI的日期选择组件 <el-date-picker>&#xff0c;当你清空所选时间时&#xff0c;组件会将绑定的 v-model 值设置为 null。这是日期选择器的预设行为&#xff0c;它将清空所选日期后将其视为 null。但有时后端不允许日期传空。 因此&#xff…...

使用模方时,三维模型在su中显示不了怎么办?

答&#xff1a;可以借助截图功能截取模型影像在su中绘制白模。 模方是一款针对实景三维模型的冗余碎片、水面残缺、道路不平、标牌破损、纹理拉伸模糊等共性问题研发的实景三维模型修复编辑软件。模方4.1新增自动单体化建模功能&#xff0c;支持一键自动提取房屋结构&#xff…...

AR-LDM原理及代码分析

AR-LDM原理AR-LDM代码分析pytorch_lightning(pl)的hook流程main.py 具体分析TrainSampleLightningDatasetARLDM blip mm encoder AR-LDM原理 左边是模仿了自回归地从1, 2, ..., j-1来构造 j 时刻的 frame 的过程。 在普通Stable Diffusion的基础上&#xff0c;使用了1, 2, .…...

MySQL常见死锁的发生场景以及如何解决

死锁的产生是因为满足了四个条件&#xff1a; 互斥占有且等待不可强占用循环等待 这个网站收集了很多死锁场景 接下来介绍几种常见的死锁发生场景。其中&#xff0c;id 为主键&#xff0c;no&#xff08;学号&#xff09;为二级唯一索引&#xff0c;name&#xff08;姓名&am…...

Leetcode 47 全排列 II

题意理解&#xff1a; 首先理解全排列是什么&#xff1f;全排列&#xff1a;使用集合中所有元素按照不同元素进行排列&#xff0c;将所有的排列结果的集合称为全排列。 这里的全排列难度升级了&#xff0c;问题在于集合中的元素是可以重复的。 问题&#xff1a;相同的元素会导致…...

C# 图解教程 第5版 —— 第18章 泛型

文章目录 18.1 什么是泛型18.2 C# 中的泛型18.3 泛型类18.3.1 声明泛型类18.3.2 创建构造类型18.3.3 创建变量和实例18.3.4 使用泛型的示例18.3.5 比较泛型和非泛型栈 18.4 类型参数的约束18.4.1 Where 子句18.4.2 约束类型和次序 18.5 泛型方法18.5.1 声明泛型方法18.5.2 调用…...

保障事务隔离级别的关键措施

目录 引言 1. 锁机制的应用 2. 多版本并发控制&#xff08;MVCC&#xff09;的实现 3. 事务日志的记录与恢复 4. 数据库引擎的实现策略 结论 引言 事务隔离级别是数据库管理系统&#xff08;DBMS&#xff09;中的一个关键概念&#xff0c;用于控制并发事务之间的可见性。…...

Docker导入导出镜像、导入导出容器的命令详解以及使用的场景

一、Docker 提供用于管理镜像和容器命令 1.1 docker save 与 docker load 这是一对操作&#xff0c;用于处理 Docker 镜像。这个操作会将所有的镜像层以及元数据打包到一个 tar 文件中。然后&#xff0c;你可以使用 docker load 命令将这个 tar 文件导入到任何 Docker 环境中…...

虚拟化嵌套

在理论上,可以在虚拟机(VM)内运行一个hypervisor,这个概念被称为嵌套虚拟化: 我们将第一个hypervisor称为Host Hypervisor,将VM内的hypervisor称为Guest Hypervisor。 在Armv8.3-A发布之前,可以通过在EL0中运行Guest Hypervisor来在VM中运行Guest Hypervisor。然而,这…...

【XILINX】记录ISE/Vivado使用过程中遇到的一些warning及解决方案

前言 XILINX/AMD是大家常用的FPGA&#xff0c;但是在使用其开发工具ISE/Vivado时免不了会遇到很多warning&#xff0c;(大家是不是发现程序越大warning越多&#xff1f;)&#xff0c;并且还有很多warning根据消除不了&#xff0c;看着特心烦&#xff1f; 我这里汇总一些我遇到的…...

Tableau进阶--Tableau数据故事慧(20)解构Tableau的绘图逻辑

官网介绍 官网连接如下&#xff1a; https://www.tableau.com/zh-cn tableau的产品包括如下&#xff1a; 参考:https://zhuanlan.zhihu.com/p/341882097 Tableau是功能强大、灵活且安全些很高的端到端的数据分析平台&#xff0c;它提供了从数据准备、连接、分析、协作到查阅…...

45.0/HTML 简介(详细版)

目录 45.1 互联网简介 45.2 网页技术与分类 45.3 HTML 简介 45.3.1 什么是 HTML?(面试题) 45.3.2 HTML 文件结构 45.3.3 HTML 语法 45.3.4 实例演练步骤(面试题) 45.4 head 中的常用标签 45.4.1 title 标记 45.4.2 meta 标记 45.4.3 45.4.4 45.4.4(面试题)总结: 45…...

Python 如何进行游戏开发?

游戏开发是一个广泛的领域&#xff0c;Python 作为一门灵活的编程语言&#xff0c;可以用于不同类型的游戏开发。以下是一些建议和步骤&#xff0c;帮助你开始使用 Python 进行游戏开发&#xff1a; 1、选择游戏开发库/框架&#xff1a; Pygame&#xff1a; Pygame 是一个用于…...

到底什么是DevOps

DevOps不是一组工具&#xff0c;也不是一个特定的岗位。在我看来DevOps更像是一种软件开发文化&#xff0c;一种实现快速交付能力的手段。 DevOps 强调的是高效组织团队之间如何通过自动化的工具协作和沟通来完成软件的生命周期管理&#xff0c;从而更快、更频繁地交付更稳定的…...

Keil生成bin文件

Keil生成bin文件_keil5生成bin文件-CSDN博客...