使用 Python 的高效相机流

news2025/1/15 22:43:59

一、说明

        让我们谈谈在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 = src
        self.fps = fps
        self.resolution = resolution
        self.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 cap

    def read(self):
        ret, frame = self.cap.read()
        if not ret:
            return None
        return frame

    def release(self):
        self.cap.release()

    def wait_for_cam(self):
        for _ in range(30):
            frame = self.read()
        if frame is not None:
            return True
        return False

2.2 使用FFmpeg

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

class VideoStreamFFmpeg:
    def __init__(self, src: int, fps: int, resolution: Tuple[int, int]):
        self.src = src
        self.fps = fps
        self.resolution = resolution
        self.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":  # macOS
            input_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 None
        image = np.frombuffer(raw_image, dtype=np.uint8).reshape(self.frame_shape)
        return image

    def release(self):
        self.pipe.terminate()

    def wait_for_cam(self):
        for _ in range(30):
            frame = self.read()
        if frame is not None:
            return True
        return 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 result

    return 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 = 30
    resolution = (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.0323s

CV2, task False:
Main function time: 3.3934s
Mean frame read time: 0.0332s

FFMPEG, task True:
Main function time: 4.461s
Mean frame read time: 0.0014s

CV2, 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 np


class VideoStreamFFmpeg:
    def __init__(self, src: int, fps: int, resolution: Tuple[int, int]):
        self.src = src
        self.fps = fps
        self.resolution = resolution
        self.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":  # macOS
            input_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 None
        image = np.frombuffer(raw_image, dtype=np.uint8).reshape(self.frame_shape)
        return image

    def release(self):
        self.pipe.terminate()

    def wait_for_cam(self):
        for _ in range(30):
            frame = self.read()
        if frame is not None:
            return True
        return False


class VideoStreamCV:
    def __init__(self, src: int, fps: int, resolution: Tuple[int, int]):
        self.src = src
        self.fps = fps
        self.resolution = resolution
        self.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 cap

    def read(self):
        ret, frame = self.cap.read()
        if not ret:
            return None
        return frame

    def release(self):
        self.cap.release()

    def wait_for_cam(self):
        for _ in range(30):
            frame = self.read()
        if frame is not None:
            return True
        return False


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 result

    return wrapper


def 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 = 30
    resolution = (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芯片上进行了测试。希望这是有帮助的!阿尔戈·萨基扬

 

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

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

相关文章

手写Spring:第6章-资源加载器解析文件注册对象

文章目录 一、目标:资源加载器解析文件注册对象二、设计:资源加载器解析文件注册对象三、实现:资源加载器解析文件注册对象3.1 工程结构3.2 资源加载器解析文件注册对象类图3.3 类工具类3.4 资源加载接口定义和实现3.4.1 定义资源加载接口3.4…

面试算法-常用数据结构

文章目录 数据结构数组链表 栈队列双端队列树 1)算法和数据结构 2)判断候选人的标准 算法能力能够准确辨别一个程序员的功底是否扎实 数据结构 数组 链表 优点: 1)O(1)时间删除或者添加 灵活分配内存空间 缺点: 2&…

把文件上传到Gitee的详细步骤

目录 第一步:创建一个空仓库 第二步:找到你想上传的文件所在的地址,打开命令窗口,git init 第三步:git add 想上传的文件 ,git commit -m "给这次提交取个名字" 第四步:和咱们在第…

生成多样、真实的评论(2019 IEEE International Conference on Big Data )

论文题目(Title):Learning to Generate Diverse and Authentic Reviews via an Encoder-Decoder Model with Transformer and GRU 研究问题(Question):评论生成,由上下文评论->生成评论 研…

Android之“写死”数据

何为“写死”,即写完之后除非手动修改,否像嘎了一样在那固定死了 在实际安卓开发中,这种写死的概念必不可少,如控件的id,某一常量,Kotlin中的Val 当然,有些需求可能也会要求我们去写死数据&am…

实战:大数据Flink CDC同步Mysql数据到ElasticSearch

文章目录 前言知识积累CDC简介CDC的种类常见的CDC方案比较 Springboot接入Flink CDC环境准备项目搭建 本地运行集群运行将项目打包将包传入集群启动远程将包部署到flink集群 写在最后 前言 前面的博文我们分享了大数据分布式流处理计算框架Flink和其基础环境的搭建&#xff0c…

入门力扣自学笔记279 C++ (题目编号:1123)

1123. 最深叶节点的最近公共祖先 题目: 给你一个有根节点 root 的二叉树,返回它 最深的叶节点的最近公共祖先 。 回想一下: 叶节点 是二叉树中没有子节点的节点树的根节点的 深度 为 0,如果某一节点的深度为 d,那它…

PyCharm中使用matplotlib.pyplot.show()报错MatplotlibDeprecationWarning的解决方案

其实这只是一个警告,忽略也可。 一、控制台输出 MatplotlibDeprecationWarning: Support for FigureCanvases without a required_interactive_framework attribute was deprecated in Matplotlib 3.6 and will be removed two minor releases later. MatplotlibD…

iOS 17中的Safari配置文件改变了游戏规则,那么如何设置呢

Safari在iOS 17中最大的升级是浏览配置文件——能够在一个应用程序中创建单独的选项卡和书签组。这些也可以跟随你的iPad和Mac,但在本指南中,我们将向你展示如何使用运行iOS 17的iPhone。 你可能有点困惑,为什么Safari中没有明显的位置可以添…

Power BI的发布到web按钮怎么没有?有人知道怎么办吗??????

Power BI的发布到web按钮怎么没有?有人知道怎么办吗????? .

使用Spring-data-jpa

EnableJpaAuditing 它是用来启动Jpa的审计功能。 jpa querydsl 多表的联合查询 导入依赖 querydsl-jpa 、querydsl-apt Repository接口, 继承QuerydslPredicateExecutor接口 NoRepositoryBean public interface BaseMongoRepository<T> extends MongoRepository<T…

GptFuck—开源Gpt4分享

这个项目不错&#xff0c;分享给大家 项目地址传送门

c语言 2.0

1.数据类型 数据类型介绍 数据类型&#xff1a;c语言中数据类型有3种&#xff0c;分别是基本数据类型、构造数据类型、指针数据类型。 数据类型的作用&#xff1a;编译器预算数据分配的内存空间大小。 ps&#xff1a;可以通俗理解为&#xff1a;数据类型是用来规范内存的开销…

避坑之路 —— 前后端 json 的注意问题

当我们在进行开发项目的时候&#xff0c;在前后端需要进行数据之间的传输&#xff0c;那么就会需要到json。而json算是字符串中的一种 1.先说一下前端的, 其实这两种都是表示前端希望能收到后端json这样的数据格式&#xff0c;那么我们在后端就需要注意将数据进行转换为json进…

Python实现猎人猎物优化算法(HPO)优化卷积神经网络回归模型(CNN回归算法)项目实战

说明&#xff1a;这是一个机器学习实战项目&#xff08;附带数据代码文档视频讲解&#xff09;&#xff0c;如需数据代码文档视频讲解可以直接到文章最后获取。 1.项目背景 猎人猎物优化搜索算法(Hunter–prey optimizer, HPO)是由Naruei& Keynia于2022年提出的一种最新的…

人工智能的优势:使用 GPT 和扩散模型生成图像

推荐&#xff1a;使用 NSDT场景编辑器快速搭建3D应用场景 世界被人工智能 &#xff08;AI&#xff09; 所吸引&#xff0c;尤其是自然语言处理 &#xff08;NLP&#xff09; 和生成 AI 的最新进展&#xff0c;这是有充分理由的。这些突破性技术有可能提高各种任务的日常生产力。…

借助各大模型的优点生成原创视频(真人人声)Plus

【技术背景】 众所周知&#xff0c;组成视频的3大元素&#xff0c;即文本语音图片。接着小编逐一介绍生成原创视频的过程。 【文本生成】 天工AI搜索&#xff08;thttp://iangong.cn&#xff09; 直接手机短信验证就可以使用&#xff0c;该大模型已经接入互联网&#xff0c…

实现CenterNet图像分割算法模型的转换和量化(SDK0301-转ONNX编译)

一、实现CenterNet图像分割算法模型的转换和量化&#xff08;SDK0301-转ONNX编译&#xff09; 1、模型转换 &#xff08;1&#xff09;下载CenterNet算法移植代码&#xff1a; $ git clone https://github.com/sophon-ai-algo/examples.git # CenterNet示例项目代码位置 /ex…

成集云 | 飞书审批同步金蝶云星空销售订单 | 解决方案

源系统成集云目标系统 方案介绍 飞书是字节跳动于2016年自研的新一代一站式协作平台&#xff0c;将即时沟通、日历、云文档、云盘和工作台深度整合&#xff0c;通过开放兼容的平台&#xff0c;让成员在一处即可实现高效的沟通和流畅的协作&#xff0c;全方位提升企业效率。 …

【vue2第十五章】VueRouter 路由配置(VueRouter)与使用 和 router-link与router-view标签使用

单页面应用 与 多页面应用 单页面应用&#xff08;Single-Page Application&#xff0c;SPA&#xff09;和多页面应用&#xff08;Multi-Page Application&#xff0c;MPA&#xff09;是 Web 应用程序的两种不同架构方式。它们在页面加载和交互方式上有所区别。 单页面应用&a…