【干货】视频文件抽帧(opencv和ffmpeg方式对比)

news2024/11/24 10:33:01

1 废话不多说,直接上代码

opencv方式

import time
import subprocess
import cv2, os
from math import ceil

def extract_frames_opencv(video_path, output_folder, frame_rate=1):
    """
    使用 OpenCV 从视频中抽取每秒指定帧数的帧,并保存到指定文件夹。
    如果视频长度不是整数秒,则会在最后一帧时补充空白图像。

    参数:
    video_path (str): 输入视频文件的路径。
    output_folder (str): 输出帧图像文件的文件夹路径。
    frame_rate (int): 每秒抽取的帧数,默认为 1。

    返回:
    None
    """
    start_time = time.time()
    # 创建输出文件夹
    os.makedirs(output_folder, exist_ok=True)

    # 打开视频文件
    cap = cv2.VideoCapture(video_path)

    # 获取视频长度和帧率
    fps = cap.get(cv2.CAP_PROP_FPS)
    total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
    duration = total_frames / fps

    # 计算需要抽取的总帧数
    target_frames = int(duration * frame_rate)

    # 逐帧抽取图像
    frame_idx = 0
    for i in range(target_frames):
        cap.set(cv2.CAP_PROP_POS_FRAMES, int(i * fps / frame_rate))
        ret, frame = cap.read()
        if ret:
            cv2.imwrite(os.path.join(output_folder, f"frame_{frame_idx:06d}.jpg"), frame)
            frame_idx += 1
        else:
            break

    # 如果最后一帧不是完整的一帧,则补充空白图像
    if frame_idx < target_frames:
        for i in range(frame_idx, target_frames):
            blank_image = 255 * np.ones((int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)), int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)), 3), dtype=np.uint8)
            cv2.imwrite(os.path.join(output_folder, f"frame_{i:06d}.jpg"), blank_image)

    # 释放视频捕获对象
    cap.release()

    print(f"成功从视频中抽取了 {target_frames} 帧, 一共耗时{time.time() - start_time}s")

ffmpeg方式

def extract_frames_ffmpeg(video_path, output_folder, frame_rate=1):
    """
    使用 FFmpeg 从视频中抽取每秒指定帧数的帧,并保存到指定文件夹。
    如果视频长度不是整数秒,则会抛出异常。

    参数:
    video_path (str): 输入视频文件的路径。
    output_folder (str): 输出帧图像文件的文件夹路径。
    frame_rate (int): 每秒抽取的帧数,默认为 1。

    返回:
    None
    """
    start_time = time.time()
    # 创建输出文件夹
    os.makedirs(output_folder, exist_ok=True)

    # 获取视频长度
    command = ["ffprobe", "-v", "error", "-show_entries", "format=duration", "-of",
               "default=nokey=1:noprint_wrappers=1", video_path]
    result = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    if result.returncode != 0:
        raise ValueError("Failed to get video duration.")
    duration = float(result.stdout.decode().strip())

    # 四舍五入视频长度到最接近的整数秒
    duration = round(duration)

    # 构建 FFmpeg 命令
    command = [
        "ffmpeg",
        "-i", video_path,
        "-vf", f"fps={frame_rate}",
        "-frames:v", "%d" % (ceil(duration * frame_rate)),
        os.path.join(output_folder, "frame_%06d.jpg")
    ]

    # 执行 FFmpeg 命令
    subprocess.run(command, check=True)

    print(f"成功从视频中抽取了 {ceil(duration * frame_rate)} 帧, 一共耗时{time.time() - start_time}s")

2 测试实验对比

测试一个56s的mp4文件

extract_frames_opencv(video_path, output_folder, 1)
# 成功从视频中抽取了 55 帧, 一共耗时10.131151914596558s
extract_frames_ffmpeg(video_path, output_folder, 1)
# 成功从视频中抽取了 56 帧, 一共耗时8.075150966644287s

 1帧/s时,ffmpeg稍快2s

extract_frames_opencv(video_path, output_folder1, 5)
# 成功从视频中抽取了 278 帧, 一共耗时54.822526931762695s
extract_frames_ffmpeg(video_path, output_folder2, 5)
# 成功从视频中抽取了 280 帧, 一共耗时8.546468019485474s

5帧/s时,ffmpeg方式只增加0.5s,opencv增加了5倍时长

在大文件抽帧、或者抽帧频率较高时,ffmpeg效率更高。

3 ffmpeg抽帧图片更小?

对比了下抽帧图片,分辨率一致,但是ffmpeg抽帧图片好像小很多,为什么呢?

搜了下官方解释:

FFmpeg 和 OpenCV 在抽取视频帧并保存图像时,可能会产生不同的结果,包括文件大小和图像质量等。出现这种差异的原因主要有以下几点:

  1. 编码器选择:

    • FFmpeg 默认使用 JPEG 编码器,这种编码方式通常可以生成较小的文件大小,但可能会略有些图像质量损失。
    • OpenCV 默认使用无损的 PNG 编码器,生成的文件通常会更大,但图像质量更好。
  2. 色彩空间转换:

    • FFmpeg 在抽取帧时,可能会将视频的色彩空间转换为更适合 JPEG 编码的格式,例如 YUV 420。这种转换可以进一步减小文件大小。
    • OpenCV 则可能会保留视频原有的色彩空间,如 RGB,在保存为 PNG 时不进行额外的转换。
  3. 图像缩放和重采样:

    • FFmpeg 在抽取帧时,可能会根据输出分辨率对图像进行缩放和重采样,从而进一步优化文件大小。
    • OpenCV 则可能会直接保存原始分辨率的图像,不进行任何缩放处理。

综上所述,FFmpeg 在抽取视频帧并保存为图像时,通常会采取一些优化措施,如使用 JPEG 编码、色彩空间转换和图像缩放等,从而生成相对较小的文件大小。而 OpenCV 则更倾向于保留原始的视觉质量,因此生成的图像文件会相对更大。

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

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

相关文章

关于队列的知识点以及例题讲解

本篇文章将带大家学习队列的相关知识点&#xff0c;并且讲解几道例题&#xff0c;请各位小伙伴耐心观看 队列的概念 队列&#xff1a;只允许在一端进行插入数据操作&#xff0c;在另一端进行删除数据操作的特殊线性表 遵循原则 先进先出 队列的实现 图解&#xff1a; 代码实…

性能监控工具

性能是任何一款软件都需要关注的重要指标。除了软件的基本功 能&#xff0c;性能可以说是评价软件优劣的最重要的指标之一。我们该如何有 效地监控和诊断性能问题呢?本章基于实践&#xff0c;着重介绍一些针对系统 和Java虚拟机的监控和诊断工具&#xff0c;以帮助读者在实际开…

软件测试——蓝桥杯笔记(自用)

Before和BeforeClass&#xff0c;在测试前&#xff0c;初始化Driver&#xff0c;BeforeClass适用于静态方法 After和AfterClass&#xff0c;在测试后&#xff0c;关闭Driver&#xff0c;AfterClass适用于静态方法 自动化测试记得使用BeforeClass&#xff0c;AfterClass 单元…

Linux应用 sqlite3编程

1、概念 SQLite3是一个轻量级的、自包含的、基于文件的数据库管理系统&#xff0c;常用于移动设备、嵌入式设备和小型应用程序中&#xff0c;应用场景如下&#xff1a; 移动应用程序&#xff1a;由于SQLite3是零配置、无服务器的数据库引擎&#xff0c;非常适合用于移动应用程…

【MySQL调优】如何进行MySQL调优?从参数、数据建模、索引、SQL语句等方向,三万字详细解读MySQL的性能优化方案(2024版)

导航&#xff1a; 本文一些内容需要聚簇索引、非聚簇索引、B树、覆盖索引、索引下推等前置概念&#xff0c;虽然本文有简单回顾&#xff0c;但详细可以参考下文的【MySQL高级篇】 【Java笔记踩坑汇总】Java基础进阶JavaWebSSMSpringBoot瑞吉外卖SpringCloud黑马旅游谷粒商城学成…

[Cesium]加载GeoJSON并自定义设置符号(以点要素为例)

数据准备&#xff1a; Geoserver发布WFS&#xff08;Web Feature Service&#xff09;服务 [GeoServer系列]Shapefile数据发布-CSDN博客 数据加载&#xff1a; 数据准备第二种方式加载数据&#xff0c;利用for循环加载多个图层。首先将获取数据(每获取一次获得pomise,将其加入…

SQL优化系列-快速学会分析SQL执行效率(下)

1 show profile 分析慢查询 有时需要确定 SQL 到底慢在哪个环节&#xff0c;此时 explain 可能不好确定。在 MySQL 数据库中&#xff0c;通过 profile&#xff0c;能够更清楚地了解 SQL 执行过程的资源使用情况&#xff0c;能让我们知道到底慢在哪个环节。 知识扩展&#xff1…

Wireshark 如何查找包含特定数据的数据帧

1、查找包含特定 string 的数据帧 使用如下指令&#xff1a; 双引号中所要查找的字符串 frame contains "xxx" 查找字符串 “heartbeat” 示例&#xff1a; 2、查找包含特定16进制的数据帧 使用如下指令&#xff1a; TCP&#xff1a;在TCP流中查找 tcp contai…

汽车分销商文件流转优化:实现稳定高效的文件分发处理

在汽车圈里&#xff0c;分销商可是个不可或缺的角色。他们既要跟汽车厂家紧紧绑在一起&#xff0c;还得跟下游的销售渠道或者直接跟消费者打成一片&#xff0c;文件来回传递那是家常便饭。 这文件发放的速度快不快&#xff0c;安不安全&#xff0c;直接影响到分销商做事的效率…

容器(Docker)安装

centos安装Docker sudo yum remove docker* sudo yum install -y yum-utils#配置docker的yum地址 sudo yum-config-manager \ --add-repo \ http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo#安装指定版本 - 可以根据实际安装版本 sudo yum install -y docke…

Centos 报错 One of the configured repositories failed

目录预览 一、问题描述二、原因分析三、解决方案四、参考链接 一、问题描述 使用yum update更新命令就出现下面问题&#xff0c;系统是刚安装的&#xff0c;然后修改了一下IP变成手动。&#xff08;排查问题前&#xff0c;先回顾自己做了哪些操作&#xff0c;方便进一步排错&a…

策略模式的理解和运用

在之前的小游戏项目中&#xff0c;处理websocket长连接请求的时候&#xff0c;需要根据传递数据包的不同类型&#xff0c;进行不同的处理。为了实现这个场景&#xff0c;比较简单的方法就是使用if-else或者switch-case语句&#xff0c;根据条件进行判断。但是这导致了项目代码复…

梦幻西游外网架设教程-端游篇

《梦幻西游》是一款由中国网易公司自行开发并营运的网络国产游戏。游戏以著名的章回小说《西游记》故事为背景&#xff0c;透过Q版的人物&#xff0c;试图营造出浪漫的网络游戏风格。 《梦幻西游》拥有注册用户超过3.1亿&#xff0c;一共开设收费服务器达472组&#xff0c;最高…

Sigmoid图像

import matplotlib.pyplot as plt import numpy as npdef sigmoid(x):# 直接返回sigmoid函数return 1. / (1. np.exp((0.7 -(x))/0.075))def plot_sigmoid():# param:起点&#xff0c;终点&#xff0c;间距x np.arange(0, 1, 1 / 16000)y sigmoid(x)plt.plot(x, y)plt.show…

【WP】猿人学_19_乌拉乌拉乌拉

https://match.yuanrenxue.cn/match/19 发包测试 经过发包测试&#xff0c;并没有携带加密参数&#xff0c;但是使用python无法复现&#xff0c;requests&#xff0c;httpx以及异步都不行&#xff0c;网上搜索了一下&#xff0c;这是使用了JA3指纹。可能是我做的时间比较晚&…

【iOS】UI学习——导航控制器、分栏控制器

UI学习&#xff08;三&#xff09; 导航控制器导航控制器基础导航控制器切换导航栏和工具栏 分栏控制器分栏控制器基础分栏控制器高级 导航控制器 导航控制器负责控制导航栏&#xff08;navigationBar&#xff09;&#xff0c;导航栏上的按钮叫UINavigationItem&#xff08;导航…

【论文阅读】SELF-RAG,让模型决策和反思检索

关于LLM何时使用RAG的问题&#xff0c;原本是阅读了关于ADAPT-LLM模型的那篇论文&#xff0c;被问到与SELF-RAG有何区别。所以&#xff0c;大概看了一下SELF-RAG这篇论文&#xff0c;确实很像&#xff0c;这些基于LLM针对下游任务的模型架构和方法&#xff0c;本来就很像。不过…

accelerate笔记:实验跟踪

Accelerate支持七种集成的跟踪器&#xff1a; TensorBoardWandBCometMLAimMLFlowClearMLDVCLive要使用这些跟踪器&#xff0c;可以通过在 Accelerator 类的 log_with 参数中传入所选类型来实现 from accelerate import Accelerator from accelerate.utils import LoggerTypeac…

Yolo-v5模型训练速度,与GeForce的AI算力描述

1.GeForce RTX3070 Ti官网参数&#xff1a; GeForce RTXTM 3070 Ti 和 RTX 3070 显卡采用第 2 代 NVIDIA RTX 架构 - NVIDIA Ampere 架构。该系列产品搭载专用的第 2 代 RT Core &#xff0c;第 3 代 Tensor Core、全新的 SM 多单元流处理器以及高速显存&#xff0c;助您在高性…

北斗RTK+UWB定位的优势

在当今科技飞速发展的时代&#xff0c;定位技术的应用已渗透到我们生活的方方面面。从导航、物流到无人驾驶、智能制造&#xff0c;精准定位技术无处不在。而北斗RTK&#xff08;Real-Time Kinematic&#xff0c;实时动态&#xff09;和UWB&#xff08;Ultra-Wideband&#xff…