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

使用 Python 的高效相机流

一、说明

        让我们谈谈在Python中使用网络摄像头。我有一个简单的任务,从相机读取帧,并在每一帧上运行神经网络。对于一个特定的网络摄像头,我在设置目标 fps 时遇到了问题(正如我现在所理解的——因为相机可以用 mjpeg 格式运行 30 fps,但不能运行原始),所以我决定深入研究 FFmpeg 看看它是否有帮助。

二、OpenCV和FFmpeg两个选项

        我最终让OpenCV和FFmpeg都工作了,但我发现了一件非常有趣的事情:FFmpeg性能优于OpenCV是我的主要用例。事实上,使用 FFmpeg,我读取帧的速度提高了 15 倍,整个管道的加速提高了 32%。我简直不敢相信结果,并多次重新检查了所有内容,但它们是一致的。

        注意:当我只是一帧一帧地读取时,性能完全相同,但是当我在读取帧后运行某些内容时,FFmpeg 速度更快(这需要时间)。我将在下面确切地说明我的意思。

2.1 openCV的代码实现

        现在,让我们看一下代码。首先 — 使用 OpenCV 读取网络摄像头帧的类:

class VideoStreamCV:def __init__(self, src: int, fps: int, resolution: Tuple[int, int]):self.src = srcself.fps = fpsself.resolution = resolutionself.cap = self._open_camera()self.wait_for_cam()def _open_camera(self):cap = cv2.VideoCapture(self.src)cap.set(cv2.CAP_PROP_FRAME_WIDTH, self.resolution[0])cap.set(cv2.CAP_PROP_FRAME_HEIGHT, self.resolution[1])fourcc = cv2.VideoWriter_fourcc(*"MJPG")cap.set(cv2.CAP_PROP_FOURCC, fourcc)cap.set(cv2.CAP_PROP_FPS, self.fps)return capdef read(self):ret, frame = self.cap.read()if not ret:return Nonereturn framedef release(self):self.cap.release()def wait_for_cam(self):for _ in range(30):frame = self.read()if frame is not None:return Truereturn False

2.2 使用FFmpeg

  我使用功能,因为相机通常需要时间“热身”。FFmpeg 类使用相同的预热:wait_for_cam

class VideoStreamFFmpeg:def __init__(self, src: int, fps: int, resolution: Tuple[int, int]):self.src = srcself.fps = fpsself.resolution = resolutionself.pipe = self._open_ffmpeg()self.frame_shape = (self.resolution[1], self.resolution[0], 3)self.frame_size = np.prod(self.frame_shape)self.wait_for_cam()def _open_ffmpeg(self):os_name = platform.system()if os_name == "Darwin":  # macOSinput_format = "avfoundation"video_device = f"{self.src}:none"elif os_name == "Linux":input_format = "v4l2"video_device = f"{self.src}"elif os_name == "Windows":input_format = "dshow"video_device = f"video={self.src}"else:raise ValueError("Unsupported OS")command = ['ffmpeg','-f', input_format,'-r', str(self.fps),'-video_size', f'{self.resolution[0]}x{self.resolution[1]}','-i', video_device,'-vcodec', 'mjpeg',  # Input codec set to mjpeg'-an', '-vcodec', 'rawvideo',  # Decode the MJPEG stream to raw video'-pix_fmt', 'bgr24','-vsync', '2','-f', 'image2pipe', '-']if os_name == "Linux":command.insert(2, "-input_format")command.insert(3, "mjpeg")return subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, bufsize=10**8)def read(self):raw_image = self.pipe.stdout.read(self.frame_size)if len(raw_image) != self.frame_size:return Noneimage = np.frombuffer(raw_image, dtype=np.uint8).reshape(self.frame_shape)return imagedef release(self):self.pipe.terminate()def wait_for_cam(self):for _ in range(30):frame = self.read()if frame is not None:return Truereturn False

For timing function, I used decorator:run

def timeit(func):def wrapper(*args, **kwargs):t0 = time.perf_counter()result = func(*args, **kwargs)t1 = time.perf_counter()print(f"Main function time: {round(t1-t0, 4)}s")return resultreturn wrapper

        作为一个繁重的合成任务,我使用了这个简单的函数来代替神经网络(它也可以只是)。这是一个非常重要的部分,因为没有任何任务,OpenCV和FFmpeg的读取速度是相同的:time.sleep

def computation_task():for _ in range(5000000):9999 * 9999

        现在功能与我读取框架的循环,它的时间,运行:computation_task

@timeit
def run(cam: VideoStreamCV | VideoStreamFFmpeg, run_task: bool):timer = []for _ in range(100):t0 = time.perf_counter()cam.read()timer.append(time.perf_counter() - t0)if run_task:computation_task()cam.release()return round(np.mean(timer), 4)

        最后,我设置了几个参数,使用 OpenCV 和 FFmpeg 初始化 2 个视频流,并在没有和使用它的情况下运行它们。maincomputation_task

def main():fsp = 30resolution = (1920, 1080)for run_task in [False, True]:ff_cam = VideoStreamFFmpeg(src=0, fps=fsp, resolution=resolution)cv_cam = VideoStreamCV(src=0, fps=fsp, resolution=resolution)print(f"FFMPEG, task {run_task}:")print(f"Mean frame read time: {run(cam=ff_cam, run_task=run_task)}s\n")print(f"CV2, task {run_task}:")print(f"Mean frame read time: {run(cam=cv_cam, run_task=run_task)}s\n")

        这是我得到的:

FFMPEG, task False:
Main function time: 3.2334s
Mean frame read time: 0.0323sCV2, task False:
Main function time: 3.3934s
Mean frame read time: 0.0332sFFMPEG, task True:
Main function time: 4.461s
Mean frame read time: 0.0014sCV2, task True:
Main function time: 6.6833s
Mean frame read time: 0.023s

        因此,如果没有合成任务,我可以获得相同的阅读时间:,。但是对于合成任务:和,所以FFmpeg要快得多。美妙之处在于,我的神经网络应用程序得到了真正的加速,而不仅仅是综合测试,所以我决定分享结果。0.03230.03320.00140.023

        下图显示了 1 次迭代所需的时间:读取帧,使用 yolov8s 模型(在 CPU 上)处理它,并使用检测到的对象保存帧:

三 完整脚本

        以下是包含综合测试的完整脚本:

import platform
import subprocess
import time
from typing import Tuple
import cv2
import numpy as npclass VideoStreamFFmpeg:def __init__(self, src: int, fps: int, resolution: Tuple[int, int]):self.src = srcself.fps = fpsself.resolution = resolutionself.pipe = self._open_ffmpeg()self.frame_shape = (self.resolution[1], self.resolution[0], 3)self.frame_size = np.prod(self.frame_shape)self.wait_for_cam()def _open_ffmpeg(self):os_name = platform.system()if os_name == "Darwin":  # macOSinput_format = "avfoundation"video_device = f"{self.src}:none"elif os_name == "Linux":input_format = "v4l2"video_device = f"{self.src}"elif os_name == "Windows":input_format = "dshow"video_device = f"video={self.src}"else:raise ValueError("Unsupported OS")command = ['ffmpeg','-f', input_format,'-r', str(self.fps),'-video_size', f'{self.resolution[0]}x{self.resolution[1]}','-i', video_device,'-vcodec', 'mjpeg',  # Input codec set to mjpeg'-an', '-vcodec', 'rawvideo',  # Decode the MJPEG stream to raw video'-pix_fmt', 'bgr24','-vsync', '2','-f', 'image2pipe', '-']if os_name == "Linux":command.insert(2, "-input_format")command.insert(3, "mjpeg")return subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, bufsize=10**8)def read(self):raw_image = self.pipe.stdout.read(self.frame_size)if len(raw_image) != self.frame_size:return Noneimage = np.frombuffer(raw_image, dtype=np.uint8).reshape(self.frame_shape)return imagedef release(self):self.pipe.terminate()def wait_for_cam(self):for _ in range(30):frame = self.read()if frame is not None:return Truereturn Falseclass VideoStreamCV:def __init__(self, src: int, fps: int, resolution: Tuple[int, int]):self.src = srcself.fps = fpsself.resolution = resolutionself.cap = self._open_camera()self.wait_for_cam()def _open_camera(self):cap = cv2.VideoCapture(self.src)cap.set(cv2.CAP_PROP_FRAME_WIDTH, self.resolution[0])cap.set(cv2.CAP_PROP_FRAME_HEIGHT, self.resolution[1])fourcc = cv2.VideoWriter_fourcc(*"MJPG")cap.set(cv2.CAP_PROP_FOURCC, fourcc)cap.set(cv2.CAP_PROP_FPS, self.fps)return capdef read(self):ret, frame = self.cap.read()if not ret:return Nonereturn framedef release(self):self.cap.release()def wait_for_cam(self):for _ in range(30):frame = self.read()if frame is not None:return Truereturn Falsedef timeit(func):def wrapper(*args, **kwargs):t0 = time.perf_counter()result = func(*args, **kwargs)t1 = time.perf_counter()print(f"Main function time: {round(t1-t0, 4)}s")return resultreturn wrapperdef computation_task():for _ in range(5000000):9999 * 9999@timeit
def run(cam: VideoStreamCV | VideoStreamFFmpeg, run_task: bool):timer = []for _ in range(100):t0 = time.perf_counter()cam.read()timer.append(time.perf_counter() - t0)if run_task:computation_task()cam.release()return round(np.mean(timer), 4)def main():fsp = 30resolution = (1920, 1080)for run_task in [False, True]:ff_cam = VideoStreamFFmpeg(src=0, fps=fsp, resolution=resolution)cv_cam = VideoStreamCV(src=0, fps=fsp, resolution=resolution)print(f"FFMPEG, task {run_task}:")print(f"Mean frame read time: {run(cam=ff_cam, run_task=run_task)}s\n")print(f"CV2, task {run_task}:")print(f"Mean frame read time: {run(cam=cv_cam, run_task=run_task)}s\n")if __name__ == "__main__":main()

注意:此脚本已在Apple的M1 Pro芯片上进行了测试。希望这是有帮助的!阿尔戈·萨基扬

 

相关文章:

使用 Python 的高效相机流

一、说明 让我们谈谈在Python中使用网络摄像头。我有一个简单的任务,从相机读取帧,并在每一帧上运行神经网络。对于一个特定的网络摄像头,我在设置目标 fps 时遇到了问题(正如我现在所理解的——因为相机可以用 mjpeg 格式运行 30…...

pycharm使用

在使用pycharm时,有时一个回车或者一个tab键,缩进的长度不符合预期可以调整设置tab键缩进的长度: 平时工作中,不同的人在编辑代码缩进的时候,有的人喜欢按四个或者六个空格,有的人喜欢按tab键,而…...

C++项目实战——基于多设计模式下的同步异步日志系统-②-相关技术补充(不定参函数)

文章目录 专栏导读不定参函数C风格不定参函数不定参宏函数 专栏导读 🌸作者简介:花想云 ,在读本科生一枚,C/C领域新星创作者,新星计划导师,阿里云专家博主,CSDN内容合伙人…致力于 C/C、Linux 学…...

iOS开发Swift-10-位置授权, cocoapods,API,天气获取,城市获取-和风天气App首页代码

1.获取用户当前所在的位置 在infi中点击加号,选择权限:当用户使用app的时候获取位置权限. 填写使用位置权限的目的. 2.获取用户的经纬度. ViewController: import UIKit import CoreLocationclass ViewController: UIViewController, CLLocationManagerDelegate { //遵循CLL…...

CNN(七):ResNeXt-50算法的思考

🍨 本文为🔗365天深度学习训练营中的学习记录博客🍖 原作者:K同学啊|接辅导、项目定制 在进行ResNeXt-50实战练习时,我也跟其他学员一样有这个疑惑,如下图所示: 反复查看代码,仍然有…...

【人月神话】深入了解软件工程和项目管理

文章目录 👨‍⚖️《人月神话》的主要观点👨‍🏫《人月神话》的主要内容👨‍💻作者介绍 🌸🌸🌸🌷🌷🌷💐💐💐&a…...

52、基于函数式方式开发 Spring WebFlux 应用

★ Spring WebFlux的两种开发方式 1. 采用类似于Spring MVC的注解的方式来开发。此时开发时感觉Spring MVC差异不大,但底层依然是反应式API。2. 使用函数式编程来开发★ 使用函数式方式开发Web Flux 使用函数式开发WebFlux时需要开发两个组件: ▲ Han…...

MySQL的用户管理

1、MySQL的用户管理 (1)创建用户 create user zhang3 identified by 123123;表示创建名称为zhang3的用户,密码设为123123。 (2)了解user表 1)查看用户 select host,user,authentication_string,select…...

LeetCode //C - 114. Flatten Binary Tree to Linked List

114. Flatten Binary Tree to Linked List Given the root of a binary tree, flatten the tree into a “linked list”: The “linked list” should use the same TreeNode class where the right child pointer points to the next node in the list and the left child …...

利用transform和border 创造简易图标,以适应uniapp中多字体大小情况下的符号问题

heml: <text class"icon-check"></text> css: .icon-check {border: 2px solid black;border-left: 0;border-top: 0;height: 12px;width: 6px;transform-origin: center;transform: rotate(45deg);} 实际上就是声明一个带边框的div 将其中相邻的两边去…...

C/C++指针函数与函数指针

一、指针函数 指针函数&#xff1a;本质为一个函数&#xff0c;返回值为指针指针函数&#xff1a;如果一个函数的返回值是指针类型&#xff0c;则称为指针函数用指针作为函数的返回值的好处&#xff1a;可以从被调函数向主函数返回大量的数据&#xff0c;常用于返回结构体指针。…...

30天入门Python(基础篇)——第1天:为什么选择Python

文章目录 专栏导读作者有话说为什么学习Python原因1(总体得说)原因2(就业说) Python的由来(来自百度百科)Python的版本 专栏导读 &#x1f525;&#x1f525;本文已收录于《30天学习Python从入门到精通》 &#x1f251;&#x1f251;本专栏专门针对于零基础和需要重新复习巩固…...

智慧公厕破解公共厕所管理的“孤岛现象”

在现代社会中&#xff0c;公共厕所是城市管理中的一项重要任务。然而&#xff0c;经常会出现公厕管理的“孤岛现象”&#xff0c;即每个公厕都是独立运作&#xff0c;缺乏统一的管理和监控机制。针对这一问题&#xff0c;智慧公厕的出现为解决公共厕所管理难题带来了新的方案。…...

excel中删除重复项

数据如图&#xff1a; 要删除姓名这一列的重复项&#xff0c;操作&#xff1a; (1)选中姓名这一列(2)点击“数据”(3)点击“删除重复项" 这是excel会自动检测出还有别的关联列 直接默认&#xff0c;点击删除重复项...弹出下面的界面 因为我们只要删除“姓名”列的重复值&…...

2023-9-8 求组合数(三)

题目链接&#xff1a;求组合数 III #include <iostream> #include <algorithm>using namespace std;typedef long long LL;int p;int qmi(int a, int k) {int res 1;while(k){if(k & 1) res (LL) res * a % p;k >> 1;a (LL) a * a % p;}return res; }…...

01 - Apache Seatunnel 源码调试

1.下载源码 https://github.com/apache/seatunnel.git2.编译 mvn clean package -pl seatunnel-dist -am -Dmaven.test.skiptrue3. 下载驱动 sh bin/install-plugin.sh 4.测试类 选择 seatunnel-examples ├── seatunnel-engine-examples ├── seatunnel-flink-connecto…...

UVA-12325 宝箱 题解答案代码 算法竞赛入门经典第二版

GitHub - jzplp/aoapc-UVA-Answer: 算法竞赛入门经典 例题和习题答案 刘汝佳 第二版 根据书上的方法来做&#xff0c;是比较简单的题目。关键在于知道等体积时的枚举法。不过数据大小可能很大&#xff0c;虽然输入可以用int处理&#xff0c;但是 体积*价值 后&#xff0c;需要l…...

烟感报警器单片机方案开发,解决方案

烟感报警器也叫做烟雾报警器。烟感报警器适用于火灾发生时有大量烟雾&#xff0c;而正常情况下无烟的场所。例如写字楼、医院、学校、博物馆等场所。烟感报警器一般安装于所需要保护或探测区域的天花板上&#xff0c;因火灾中烟雾比空气轻&#xff0c;更容易向上飘散&#xff0…...

【JavaEE】_CSS引入方式与选择器

目录 1. 基本语法格式 2. 引入方式 2.1 内部样式 2.2 内联样式 2.3 外部样式 3. 基础选择器 3.1 标签选择器 3.2 类选择器 3.3 ID选择器 4. 复合选择器 4.1 后代选择器 4.2 子选择器 4.3 并集选择器 4.4 伪类选择器 1. 基本语法格式 选择器若干属性声明 2. 引入…...

【8】shader写入类中

上一篇将 vao vbo写入类中进行封装&#xff0c;本篇将shader进行封装。 Shader shader("res/shaders/Basic.shader");shader.Bind(); shader.SetUniform4f("u_Color", 0.2f, 0.3f, 0.8f, 1.0f);shader.h #pragma once#include <string> #include &l…...

Vue3 + Element Plus + TypeScript中el-transfer穿梭框组件使用详解及示例

使用详解 Element Plus 的 el-transfer 组件是一个强大的穿梭框组件&#xff0c;常用于在两个集合之间进行数据转移&#xff0c;如权限分配、数据选择等场景。下面我将详细介绍其用法并提供一个完整示例。 核心特性与用法 基本属性 v-model&#xff1a;绑定右侧列表的值&…...

React Native在HarmonyOS 5.0阅读类应用开发中的实践

一、技术选型背景 随着HarmonyOS 5.0对Web兼容层的增强&#xff0c;React Native作为跨平台框架可通过重新编译ArkTS组件实现85%以上的代码复用率。阅读类应用具有UI复杂度低、数据流清晰的特点。 二、核心实现方案 1. 环境配置 &#xff08;1&#xff09;使用React Native…...

【C语言练习】080. 使用C语言实现简单的数据库操作

080. 使用C语言实现简单的数据库操作 080. 使用C语言实现简单的数据库操作使用原生APIODBC接口第三方库ORM框架文件模拟1. 安装SQLite2. 示例代码:使用SQLite创建数据库、表和插入数据3. 编译和运行4. 示例运行输出:5. 注意事项6. 总结080. 使用C语言实现简单的数据库操作 在…...

tree 树组件大数据卡顿问题优化

问题背景 项目中有用到树组件用来做文件目录&#xff0c;但是由于这个树组件的节点越来越多&#xff0c;导致页面在滚动这个树组件的时候浏览器就很容易卡死。这种问题基本上都是因为dom节点太多&#xff0c;导致的浏览器卡顿&#xff0c;这里很明显就需要用到虚拟列表的技术&…...

【SSH疑难排查】轻松解决新版OpenSSH连接旧服务器的“no matching...“系列算法协商失败问题

【SSH疑难排查】轻松解决新版OpenSSH连接旧服务器的"no matching..."系列算法协商失败问题 摘要&#xff1a; 近期&#xff0c;在使用较新版本的OpenSSH客户端连接老旧SSH服务器时&#xff0c;会遇到 "no matching key exchange method found"​, "n…...

比较数据迁移后MySQL数据库和OceanBase数据仓库中的表

设计一个MySQL数据库和OceanBase数据仓库的表数据比较的详细程序流程,两张表是相同的结构,都有整型主键id字段,需要每次从数据库分批取得2000条数据,用于比较,比较操作的同时可以再取2000条数据,等上一次比较完成之后,开始比较,直到比较完所有的数据。比较操作需要比较…...

Python网页自动化Selenium中文文档

1. 安装 1.1. 安装 Selenium Python bindings 提供了一个简单的API&#xff0c;让你使用Selenium WebDriver来编写功能/校验测试。 通过Selenium Python的API&#xff0c;你可以非常直观的使用Selenium WebDriver的所有功能。 Selenium Python bindings 使用非常简洁方便的A…...

CSS3相关知识点

CSS3相关知识点 CSS3私有前缀私有前缀私有前缀存在的意义常见浏览器的私有前缀 CSS3基本语法CSS3 新增长度单位CSS3 新增颜色设置方式CSS3 新增选择器CSS3 新增盒模型相关属性box-sizing 怪异盒模型resize调整盒子大小box-shadow 盒子阴影opacity 不透明度 CSS3 新增背景属性ba…...

对象回调初步研究

_OBJECT_TYPE结构分析 在介绍什么是对象回调前&#xff0c;首先要熟悉下结构 以我们上篇线程回调介绍过的导出的PsProcessType 结构为例&#xff0c;用_OBJECT_TYPE这个结构来解析它&#xff0c;0x80处就是今天要介绍的回调链表&#xff0c;但是先不着急&#xff0c;先把目光…...

FOPLP vs CoWoS

以下是 FOPLP&#xff08;Fan-out panel-level packaging 扇出型面板级封装&#xff09;与 CoWoS&#xff08;Chip on Wafer on Substrate&#xff09;两种先进封装技术的详细对比分析&#xff0c;涵盖技术原理、性能、成本、应用场景及市场趋势等维度&#xff1a; 一、技术原…...