使用 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 个视频流,并在没有和使用它的情况下运行它们。main
computation_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.0323
0.0332
0.0014
0.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++指针函数与函数指针
一、指针函数 指针函数:本质为一个函数,返回值为指针指针函数:如果一个函数的返回值是指针类型,则称为指针函数用指针作为函数的返回值的好处:可以从被调函数向主函数返回大量的数据,常用于返回结构体指针。…...

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

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

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

2023-9-8 求组合数(三)
题目链接:求组合数 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: 算法竞赛入门经典 例题和习题答案 刘汝佳 第二版 根据书上的方法来做,是比较简单的题目。关键在于知道等体积时的枚举法。不过数据大小可能很大,虽然输入可以用int处理,但是 体积*价值 后,需要l…...

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

【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写入类中进行封装,本篇将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…...

AI-调查研究-01-正念冥想有用吗?对健康的影响及科学指南
点一下关注吧!!!非常感谢!!持续更新!!! 🚀 AI篇持续更新中!(长期更新) 目前2025年06月05日更新到: AI炼丹日志-28 - Aud…...

基于FPGA的PID算法学习———实现PID比例控制算法
基于FPGA的PID算法学习 前言一、PID算法分析二、PID仿真分析1. PID代码2.PI代码3.P代码4.顶层5.测试文件6.仿真波形 总结 前言 学习内容:参考网站: PID算法控制 PID即:Proportional(比例)、Integral(积分&…...

Prompt Tuning、P-Tuning、Prefix Tuning的区别
一、Prompt Tuning、P-Tuning、Prefix Tuning的区别 1. Prompt Tuning(提示调优) 核心思想:固定预训练模型参数,仅学习额外的连续提示向量(通常是嵌入层的一部分)。实现方式:在输入文本前添加可训练的连续向量(软提示),模型只更新这些提示参数。优势:参数量少(仅提…...
1688商品列表API与其他数据源的对接思路
将1688商品列表API与其他数据源对接时,需结合业务场景设计数据流转链路,重点关注数据格式兼容性、接口调用频率控制及数据一致性维护。以下是具体对接思路及关键技术点: 一、核心对接场景与目标 商品数据同步 场景:将1688商品信息…...

【开发技术】.Net使用FFmpeg视频特定帧上绘制内容
目录 一、目的 二、解决方案 2.1 什么是FFmpeg 2.2 FFmpeg主要功能 2.3 使用Xabe.FFmpeg调用FFmpeg功能 2.4 使用 FFmpeg 的 drawbox 滤镜来绘制 ROI 三、总结 一、目的 当前市场上有很多目标检测智能识别的相关算法,当前调用一个医疗行业的AI识别算法后返回…...

【JVM面试篇】高频八股汇总——类加载和类加载器
目录 1. 讲一下类加载过程? 2. Java创建对象的过程? 3. 对象的生命周期? 4. 类加载器有哪些? 5. 双亲委派模型的作用(好处)? 6. 讲一下类的加载和双亲委派原则? 7. 双亲委派模…...

解读《网络安全法》最新修订,把握网络安全新趋势
《网络安全法》自2017年施行以来,在维护网络空间安全方面发挥了重要作用。但随着网络环境的日益复杂,网络攻击、数据泄露等事件频发,现行法律已难以完全适应新的风险挑战。 2025年3月28日,国家网信办会同相关部门起草了《网络安全…...
tomcat入门
1 tomcat 是什么 apache开发的web服务器可以为java web程序提供运行环境tomcat是一款高效,稳定,易于使用的web服务器tomcathttp服务器Servlet服务器 2 tomcat 目录介绍 -bin #存放tomcat的脚本 -conf #存放tomcat的配置文件 ---catalina.policy #to…...

关于easyexcel动态下拉选问题处理
前些日子突然碰到一个问题,说是客户的导入文件模版想支持部分导入内容的下拉选,于是我就找了easyexcel官网寻找解决方案,并没有找到合适的方案,没办法只能自己动手并分享出来,针对Java生成Excel下拉菜单时因选项过多导…...
LOOI机器人的技术实现解析:从手势识别到边缘检测
LOOI机器人作为一款创新的AI硬件产品,通过将智能手机转变为具有情感交互能力的桌面机器人,展示了前沿AI技术与传统硬件设计的完美结合。作为AI与玩具领域的专家,我将全面解析LOOI的技术实现架构,特别是其手势识别、物体识别和环境…...