OpenCV实现按指定间隔抽取视频中的图像帧

2023-05-16

习惯了C++语言的OpenCV突然用Python语言OpenCV还是感觉有点不适应,但是慢慢在写的过程中,觉得Python语言的风格也挺美的(但自己的写的还是很丑…),晚上回宿舍的剩余时间,记录一下最近用PythonOpenCV实现的视频抽帧小工具。

实现代码

2022/9/26更新:将while循环中连续帧读取再根据条件保存关键帧的代码改为通过OpenCVVideoCapture::set()函数传入cv2.CAP_PROP_POS_FRAMES参数和帧间隔实现每次循环读取直接跳到关键帧并保存(其实就是快进功能)

参数解读:
cv2.CAP_PROP_POS_FRAMES: 接下来要解码/捕获的帧(基于0的索引)

代码就是逻辑关系,简单记录一下:

import cv2
import os
import numpy as np


# video所在的根目录
video_base_path = "F:/my_Video/RowVideo"
# processVideo所在根目录
save_base_path = "F:/my_Video/ProcessVideo"
# 保存抽取的图像帧时所在的根目录
JPEGImage_base_path = "./JPEGImage"

# ******************指定提取图像帧的模式********************* #
# model = 0 以帧间隔提取, model = 1以秒间隔提取
extract_frame_model = 0

def form_single_channel_video(video_name: str, save_name: str, channel: int):
    """
    提取RGB中某通道形成一个单通道图像
    :param video_name: 输入根目录文件夹下video_name,eg. test.mp4
    :param save_name: 输入保存视频的name,eg. protest.mp4
    :param channel: 需要的提取的channel -->(0, 1, 2)
    :return: None
    """
    video_path = os.path.join(video_base_path, video_name)
    save_path = os.path.join(save_base_path, save_name)

    inputVideo = cv2.VideoCapture(video_path)
    if not inputVideo.isOpened():
        raise IOError("current video path is not exist!")

    ex = int(inputVideo.get(cv2.CAP_PROP_FOURCC))
    Size = (int(inputVideo.get(cv2.CAP_PROP_FRAME_WIDTH)), int(inputVideo.get(cv2.CAP_PROP_FRAME_HEIGHT)))
    fps = int(inputVideo.get(cv2.CAP_PROP_FPS))
    total_frame = inputVideo.get(cv2.CAP_PROP_FRAME_COUNT)
    # 写入视频的文件路径 opencv的Size参数是(width,height)
    outputVideo = cv2.VideoWriter(save_path, ex, fps, Size, True)
    if not outputVideo.isOpened():
        raise IOError("no way to open the save path, please restart")

    print("input frame resolution: width=%d  height=%d, fps=%d, the total frame=%d"
          % (Size[0], Size[1], fps, total_frame))

    while inputVideo.isOpened():
        ret, frame = inputVideo.read()
        if not ret:
            break

        (B, G, R) = cv2.split(frame)
        zeros = np.zeros(np.shape(frame)[:2], dtype="uint8")
        output = np.zeros(np.shape(frame), dtype="uint8")
        if channel == 0:
            output = cv2.merge([B, zeros, zeros])
        elif channel == 1:
            output = cv2.merge([zeros, G, zeros])
        else:
            output = cv2.merge([zeros, zeros, R])

        # 将帧写入视频
        outputVideo.write(output)

        cv2.imshow("frame", frame)
        cv2.imshow("output", output)
        key = cv2.waitKey(1)
        if key == ord('q'):
            break

    print("clearing up!")
    cv2.destroyAllWindows()
    inputVideo.release()
    outputVideo.release()


def plus_shot_video_frame(video_name: str, image_save_path: str = None, frequency: int = 1, second: int = 1):
    """
    根据mode实现的不同形式的抽帧方式,当mode==0,按指定的帧间隔抽帧,mode==1,按指定的秒间隔进行抽帧
    :param video_name: 输入根目录文件夹下的video name,eg. test.mp4
    :param image_save_path: 当为None时默认在video所在文件中创建一个同名一个与video同名的文件中用于保存图像帧,否则输入绝对路径
    :param frequency: 当mode为0时,该参数启用,表示设置的帧间隔
    :param second: 当mode为1时,该参数启用,表示设置的秒间隔
    :return: None
    """
    actual_video_name = video_name.split('.')[0]  # 提取真正的文件夹名称
    if image_save_path is None:
        image_save_path = os.path.join(video_base_path, actual_video_name)

    if not os.path.exists(image_save_path):
        os.makedirs(image_save_path)

    video_path = os.path.join(video_base_path, video_name)
    inputVideo = cv2.VideoCapture(video_path)
    if not inputVideo.isOpened():
        raise IOError("current video path is not exist!")

    Size = (int(inputVideo.get(cv2.CAP_PROP_FRAME_WIDTH)), int(inputVideo.get(cv2.CAP_PROP_FRAME_HEIGHT)))
    fps = int(inputVideo.get(cv2.CAP_PROP_FPS))
    total_frame = inputVideo.get(cv2.CAP_PROP_FRAME_COUNT)
    print("input frame resolution: width=%d  height=%d, fps=%d, the total frame=%d"
          % (Size[0], Size[1], fps, total_frame))

    num_frame = 0  # 帧计数
    if extract_frame_model == 0:
        frame_gap = frequency
    else:
        frame_gap = int(second * fps)

    start_time = time.time()                # 时间测算
    # 循环读取每一帧,根据model来确定抽帧模式
    while True:
        ret_success, frame = inputVideo.read()
        if not ret_success:
            print("extract frame from video fail, current frame=%d" % num_frame)
            break
            
        # frame = cv2.resize(frame, dsize=None, fx=0.5, fy=0.5, interpolation=cv2.INTER_LINEAR)
        image_name = actual_video_name + str(round(num_frame / frame_gap)) + '.jpg'
        cv2.imwrite(os.path.join(image_save_path, image_name), frame)
        print("saved " + str(round(num_frame / frame_gap)) + '.jpg')

        num_frame += frame_gap  # 更新帧计数
        inputVideo.set(cv2.CAP_PROP_POS_FRAMES, num_frame)      # 隔帧读取

    end_time = time.time()
    print("time consuming: {:.2f}second".format(end_time - start_time))

    print("cleaning up!")
    inputVideo.release()



if __name__ == "__main__":
    video_name = 'shotTestVideo.MP4'
    shot_video_frame(video_name, frequency=20)

实现结果

终端打印
在这里插入图片描述

图片文件夹
在这里插入图片描述

总结

发现对于视频流的解码过程几乎不了解,对于倍数播放,当视频分辨率过大(4K)时,几乎没有效果,要想达到potplayer的效果还有很长的一段路要走。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

OpenCV实现按指定间隔抽取视频中的图像帧 的相关文章

随机推荐

  • 多线程经典练习题

    线程练习题 1 用共享资源的方式实现 生产者 仓库 消费者的模式 注意线程安全 线程通信 span class token keyword public span span class token keyword class span sp
  • 目标检测1——YOLO数据标注以及xml转为txt文件脚本实战

    目录 1 数据标注 2 xml批量转为txt文件 1 数据标注 利用Yolov5进行目标检测的过程中 我们需要对数据进行标注 这里我们利用的是labelImg脚本进行数据标注的 labelImg主要用于yolov5数据标注工具 深度学习文档
  • JSON数据清理(详解)

    二 JSON数据清洗 1 JSON数据 仅以两条数据为例 span class token number 1593136280858 span span class token operator span span class token
  • Spark-SQL常用内置日期时间函数

    Spark SQL常用内置日期时间函数 一 获取当前时间 1 current date 获取当前日期时间格式 xff1a yyyy MM dd spark span class token punctuation span span cla
  • Java连接hive

    注意 xff1a 需要开启hive服务 首先建一个maven工程 xff0c 导入依赖包导pom xml span class token tag span class token tag span class token punctuat
  • 基于 Mapnik 的地图服务器

    目录 一 简介二 安装 PostgreSQL 数据库和 PostGIS 扩展三 下载地图样式表和上传地图数据四 将地图数据导入 PostgresSQL五 生成 Mapnik Stylesheet六 安装 mapnik七 地图生成1 安装 E
  • Linux 添加互信

    一 添加主机列表 span class token function vi span etc hosts 添加内容 ip1 hoatnmae1 ip2 hoatnmae2 ip3 hoatnmae3 二 秘钥分发 2 1 生成秘钥 ssh
  • Python升级后 yum 无法正常使用

    问题 xff1a 原因 xff1a 这是因为 yum 采用 Python 作为命令解释器 xff0c 这可以从 usr bin yum 文件中第一行 usr bin python 发现 而 python 版本之间兼容性不太好 xff0c 使
  • CDH开启高可用后,NameNode主备节点切换

    查看nn1的状态 hdfs haadmin getServiceState nn1 span class token comment standby span hdfs haadmin getServiceState nn2 span cl
  • git分支从master切换到main

    git分支从master切换到main 背景 本地当前分支为master xff0c 远程仓库为main xff0c 且远程仓库与本地仓库有 unrelated histories这样的问题 xff0c 如远程仓库有README md但本地
  • Docker学习第三天——Docker网络

    文章目录 摘要Linux 网络命名空间Docker bridge网络容器之间的Link容器的端口映射容器网络之host和none多容器复杂应用的部署多机器通信 摘要 Docker学习之旅第三天 Docker 网络 看完这篇文章将收获dock
  • 大津阈值法(OTSU)功能实现

    具体的公式推导参见冈萨雷斯 数字图像处理 Otsu方法又称最大类间方差法 xff0c 通过把像素分配为两类或多类 xff0c 计算类间方差 xff0c 当方差达到最大值时 xff0c 类分割线 xff08 即灰度值 xff09 就作为图像分
  • cas-server服务端搭建

    1 下载cas服务代码 xff0c https github com apereo cas overlay template tree 5 3 2 使用idea工具打开cas overlay template 5 3包 xff0c 使用ma
  • 自适应阈值canny边缘检测(功能实现)

    学习记录 1 概述 canny边缘检测是一种特别常用且性能优秀的边缘检测算法 xff0c 相比于普通的边缘检测算法 xff0c canny获得的边缘较细且具有连续的边缘轮廓 xff0c 为之后的一系列图像处理带来极大的便利 canny边缘检
  • OpenCV实现图像基础频率域滤波

    写在前面 xff1a 刚开始接触数字图像处理频率域滤波时 xff0c 很是陌生 xff0c 感觉这个技术使用范围很窄 xff0c 不如空域直接处理来的实在 xff0c 最近看书发现有些情况又不得不在频率域中进行操作 xff0c 个人感觉图像
  • OpenCV实现同态滤波

    同态滤波是属于图像增强的一个小算法 xff0c 其原理和代码实现在众多博客中均有提及 xff0c 再此 xff0c 只对学习中一些自认为有用的知识点进行总结 实现和学习过程中的一些总结 xff1a 同态滤波类似于灰度变换 xff0c 都是对
  • OpenCV实现击中击不中变换和形态学细化

    1 击中击不中变换 1 1 HMT概述 形态学Hit or Miss是形状检测基本工具 xff0c 只要结构元设置得当 xff0c 就可以检测一些基本的形状图案 xff0c HMT变换只能作用于二值图像 xff0c 结构元 xff08 核
  • OpenCV综合练习1——水瓶水位线合格检测

    数字图像处理综合练习 水瓶水位线合格检测 马上就要转到学习深度学习的主干线了 xff0c 这也是大势所趋 xff0c 但不能忘本 xff0c 传统图像处理的知识也是非常重要的 xff0c 特此记录一下之前学习时做过的小练习 整个项目的资源放
  • 目标检测学习1——iou计算与非极大值抑制NMS

    刚开始学习目标检测 xff0c 都是在学习一些经典的目标检测框架 xff0c 个人认为在大量阅读和理解别人现成的代码时 xff0c 也要懂得去动手模仿 xff0c 尝试着去修改别人的代码 xff0c 即使是自己抄一遍别人的代码 xff0c
  • OpenCV实现按指定间隔抽取视频中的图像帧

    习惯了C 43 43 语言的OpenCV突然用Python语言OpenCV还是感觉有点不适应 xff0c 但是慢慢在写的过程中 xff0c 觉得Python语言的风格也挺美的 但自己的写的还是很丑 xff0c 晚上回宿舍的剩余时间 xff0