基于Python的pyAV读取H265(HEVC)编码的视频文件

1.问题出现        

        利用海康威视相机拍出来的视频是H265格式的,相比于常规的H264编码,压缩率更高,但因此如果直接用之前的方法读取,会出现无法读取的情况,如下。

        可以看到,对于帧间没有改变的部分,H265编码就只保存一份,因此直接解析出来就都是空白的,只保存了当前帧中运动(不同的部分)。这样的话,采用常规的OpenCV读取视频的方式就行不通了。因此本篇博客主要介绍另一种更“专业”的视频读取方法:利用PyAV读取视频。另外也对之前的OpenCV读取视频文件代码进行了优化,效率会高很多。具体来说就是直接使用get()函数根据索引直接获取到指定帧即可,而不再需要逐帧遍历。

2.PyAV简介与安装

        简单来说,PyAV是对FFmpeg的Python封装,集成了FFmpeg的全部功能,使用起来十分方便。如果对视频处理稍微有点了解的话,应该听过FFmpeg,官网是这里,它是一个跨平台的多媒体处理工具,很多一些小的视频播放器都是基于它开发的。PyAV的官网是这里。相比于前面提到的OpenCV,它是一个专门用来处理视频的库,因此各种功能十分专业,包括转码、读取音频等。它的安装也十分简单,pip即可。

pip install av

        对,它的简写就是av,安装好以后就可以愉快地写代码了。

3.H265视频读取代码

        利用PyAV读取H265视频文件的核心代码如下。

import av

if __name__ == '__main__':
    video_path = "./D01_20201011131153.mp4"
    out_path = "./frames"
    frame_interval = 500

    container = av.open(video_path)
    # 获取要提取的视频流对象
    stream = container.streams.video[0]
    fps = stream.base_rate  # 帧率
    frame_width = stream.width  # 帧宽
    frame_height = stream.height  # 帧高
    total_time_in_second = stream.duration * 1.0 * stream.time_base  # 视频总长
    total_frame = int(total_time_in_second * fps) + 1  # 视频总帧数
    iter_time = int(total_frame / frame_interval) + 1  # 需要迭代的次数
    print('Total time in second:', round(total_time_in_second, 3))

    counter = 0
    frame_indices = []
    for frame in container.decode(video=0):
        if frame.index % frame_interval == 0:
            counter += 1
            frame_indices.append(frame.index)
            print(counter, '/', iter_time)
            # to_image()函数返回的其实是PIL类型的影像,因此,这里的save()函数其实是PIL的Image类型的成员函数,和PyAV无关
            # 因此,如果需要修改什么保存相关设置的话,按照PIL的API进行
            frame.to_image().save(out_path + "/frame-%05d.jpg" % frame.index)

    fout = open(out_path + "/summary.txt", 'w')
    fout.write("Video name:" + video_path + "\n")
    fout.write("Frames in total:" + total_frame.__str__() + "\n")
    fout.write("FPS:" + fps.__str__() + "\n")
    fout.write("Seconds in total:" + round(total_time_in_second, 3).__str__() + "\n")
    fout.write("Frame width:" + frame_width.__str__() + "\n")
    fout.write("Frame height:" + frame_height.__str__() + "\n")
    fout.write("Output frame number:" + iter_time.__str__() + "\n")
    for i in range(len(frame_indices)):
        fout.write(frame_indices[i].__str__() + "\t" + round(frame_indices[i] * 1.0 / fps, 3).__str__() + "\n")
    fout.close()

    print("\n==========Summary Info==========")
    print('Frames in total:', total_frame)
    print('FPS:', fps)
    print('Frame width:', frame_width)
    print('Frame height', frame_height)
    print("Output frame number:", len(frame_indices))

        输出的帧如下。

        除了输出的帧影像,还会保存输出的每一帧所对应的时间,方便其它后续应用。当然,如果仔细研究的话可以看到,这里其实还是非常简单粗暴地读取每一帧,然后选择指定帧保存。因为目前我并不知道如何根据索引来定位某一帧,因此只能用这样的办法了。如果以后了解的话,会及时更新。

4.优化版OpenCV代码

        把之前循环遍历的步骤简化了,这样就不用逐帧读取,直接获取指定位置的帧内容就好了。

import cv2

if __name__ == '__main__':
    video_path = "./D01_20201011131153.mp4"
    out_path = "./frames"
    out_type = ".jpg"
    time_interval = 50

    cap = cv2.VideoCapture(video_path)
    frames = int(cap.get(7))
    fps = int(cap.get(5))
    video_width = int(cap.get(3))
    video_height = int(cap.get(4))
    frame_interval = time_interval * fps

    total_number = int(round(frames / frame_interval, 0))

    frame_indices = []
    for i in range(0, frames, frame_interval):
        cap.set(cv2.CAP_PROP_POS_FRAMES, i)
        ret, frame = cap.read()
        print('Total frame number:' + total_number.__str__(), ', complete', round((i * 1.0 / frames) * 100, 3), '%')
        cv2.imwrite(out_path + "/frame_" + i.__str__().zfill(5) + out_type, frame)
        frame_indices.append(i)
    cap.release()

    fout = open(out_path + "/summary.txt", 'w')
    fout.write("Video name:" + video_path + "\n")
    fout.write("Frames in total:" + frames.__str__() + "\n")
    fout.write("FPS:" + fps.__str__() + "\n")
    fout.write("Seconds in total:" + round(frames * 1.0 / fps, 3).__str__() + "\n")
    fout.write("Frame width:" + video_width.__str__() + "\n")
    fout.write("Frame height:" + video_height.__str__() + "\n")
    fout.write("Output frame number:" + len(frame_indices).__str__() + "\n")
    for i in range(len(frame_indices)):
        fout.write(frame_indices[i].__str__() + "\t" + round(frame_indices[i] * 1.0 / fps, 3).__str__() + "\n")
    fout.close()

    print("\n==========Summary Info==========")
    print('Frames in total:', frames)
    print('FPS:', fps)
    print('Frame width:', video_width)
    print('Frame height', video_height)
    print("Output frame number:", len(frame_indices))

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

AD教程 (十一)封装的统一管理

AD教程 (十一)封装的统一管理 PCB封装添加 一个一个手动添加,效率太低,不建议使用 使用封装管理器快速添加,根据BOM表(元器件清单),修改PCB封装 点击工具,选择封装管理器,进入封装…

全局前置路由守卫(beforeEach)

全局前置路由守卫(beforeEach) 功能:每一次切换任意路由组件之前都会被调用,相当于在进入另一个路由组件之前设置一个权限。 路由守卫的存在意义就是在不同的时间,不同的位置,去添加代码。如:J…

【开源】基于Vue和SpringBoot的生活废品回收系统

项目编号: S 003 ,文末获取源码。 \color{red}{项目编号:S003,文末获取源码。} 项目编号:S003,文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、研究内容三、界面展示3.1 登录注册3.2 资源类型&…

黑马程序员微服务SpringCloud实用篇02

SpringCloud实用篇02 0.学习目标 1.Nacos配置管理 Nacos除了可以做注册中心,同样可以做配置管理来使用。 1.1.统一配置管理 当微服务部署的实例越来越多,达到数十、数百时,逐个修改微服务配置就会让人抓狂,而且很容易出错。我…

创新无处不在的便利体验——基于智能视频和语音技术的安防监控系统EasyCVR

随着科技的迅猛发展,基于智能视频和语音技术的EasyCVR智能安防监控系统正以惊人的速度改变我们的生活。EasyCVR通过结合先进的视频分析、人工智能和大数据技术,为用户提供了更加智能、便利的安全保护体验,大大提升了安全性和便利性。本文将介…

java数据结构--阻塞队列

目录 一.概念 二.生产者消费者问题 三.阻塞队列接口BlockingQueue 四.基于数组实现单锁的阻塞队列 1.加锁方式 2.代码实现 3.解释说明 (1).offer添加元素 (2)poll取出元素 4.timeout超时时间 5.测试 五.基于数组实现双锁的阻塞队列 1.问题 …

四.pyqt5 登录界面和功能

一.使用qt creator 设置登录界面 主界面为之前设计的界面 from123.py 文章地址:三.listview或tableviw显示 二.导出ui文件为py文件 # from123.py 为导出 py文件 form.ui 为 qt creator创造的 ui 文件 pyuic5 -o x:\xxx\Fromlogin20230809.py form.ui三.python 显…

中断处理程序的延迟可能导致中断标志位仍然被置位

当中断处理程序的执行时间超过了中断事件的频率时,可能出现中断标志位仍然被置位的情况。让我们来详细解释一下这种情况。 在一个典型的系统中,中断处理程序会在中断事件发生时被触发执行。中断处理程序负责处理中断事件,并可能执行一系列操…

mac的可清除空间(时间机器)

看到这个可用82GB(458.3MB可清除) 顿时感觉清爽,之前的还是可用82GB(65GB可清除),安装个xcode都安装不上,费解半天,怎么都解决不了这个问题,就是买磁盘情理软件也解决不了…

网络运维Day06-补充

文章目录 RAID磁盘阵列RAID0条带模式RAID1镜像模式RAID5高性价比模式RAID01RAID10 逻辑卷一块磁盘的使用流程逻辑卷的使用流程 制作逻辑卷步骤一:添加硬盘步骤二:分区规划步骤三:制作物理卷步骤四:制作卷组步骤五:制作…

PHP网站源码 知识付费分站代理自助下单系统花粥商城放墙带知识付费模版

花粥商城,自带防墙,本人一直在用,没有被墙过,自带知识付费模版美化版,用户登录的页面也很好看 上传商城源码,再把知识付费模版上传到根目录 访问域名,后台地址:域名/admin 登录账…

opencv4笔记

图像二值化 全局法Threshold 大津法 大津法OSTU阈值类型——适用于双峰直方图 OTSU算法也称最大类间差法,由大津于1979年提出,被认为是图像分割中阈值选取的最佳算法,计算简单,不受图像亮度和对比度的影响,它是按图…

关于Maven中pom.xml文件不报错但无法导包解决方法

问题 我的pom文件没有报红&#xff0c;但是依赖无法正常导入。 右下角还总出现这种问题。 点开查看报错日志。大致如下 1) Error injecting constructor, java.lang.NoSuchMethodError: org.apache.maven.model.validation.DefaultModelValidator: method <init>()V no…

关于锁策略

常见的锁策略悲观锁乐观锁读写锁轻量级锁、重量级锁自旋锁公平锁和非公平锁可重入锁 vs 不可重入锁synchronized是什么锁呢&#xff1f; 常见的锁策略 锁策略不仅仅限制于Java;其它锁相关的也是会涉及这些策略;这些特性主要是在实现锁的时候运用的。虽然我们的工作可能就是把轮…

virtualBox虚拟机局域网访问配置

在VirtualBox中&#xff0c;桥接网络是一种网络连接类型&#xff0c;它允许虚拟机连接到物理网络上的路由器或交换机&#xff0c;在物理网络上获得独立的网络地址和访问权限。 一、设置VirtualBox桥接网络的步骤&#xff1a; 打开VirtualBox软件&#xff0c;并选择你想要配置…

FPGA运算

算数运算中&#xff0c;输入输出的负数全用补码来表示&#xff0c;例如用三位小数位来表示的定点小数a-1.625和b-1.375。那么原码分别为a6b‘101101, b6b101011, 补码分别是a6’b110011&#xff0c;b6‘b110101&#xff1b; 如果想在fpga中实现a*b&#xff0c;则需要将a和b用补…

文件夹批量改名:实用技巧,如何快速删除文件夹名称中的数字

在我们的日常生活和工作中&#xff0c;文件夹命名经常会包含一些数字&#xff0c;这些数字可能是在文件夹创建时自动添加的&#xff0c;也可能是为了方便我们识别而手动添加的。然而&#xff0c;有时候这些数字可能会变得不再必要&#xff0c;或者我们想要删除它们以使文件夹名…

浅聊反射系数——为何有共轭?

文章目录 1、基于行波理论的行波反射系数2、共轭匹配与功率波反射系数3、总结 不知道大家是否有和我一样&#xff0c;有下列疑惑&#xff1a;为什么反射系数定义中分子有的时候存在共轭&#xff0c;有的时候又没有共轭。比如&#xff1a; 通俗解释就是&#xff1a;一般来说&…

IDEA 使用Reset Current Branch to Here 进行git 版本控制,图文操作

文章目录 一、总结区别&#xff08;只针对本地仓库操作&#xff09;Soft详细解释文件版本冲突处理 Mixed详细解释Hard详细解释Keep详细解释文件版本冲突处理 二、其他Revert commit 参考文档 一、总结区别&#xff08;只针对本地仓库操作&#xff09; Soft详细解释 Soft操作只…

Spring高手之路16——解析Spring XML配置的BeanDefinition源码

文章目录 1. BeanDefinition阶段的分析2. 加载xml配置文件2.1 XML配置文件中加载bean的代码示例2.2 setConfigLocations - 设置和保存配置文件路径2.3 refresh - 触发容器刷新&#xff0c;配置文件的加载与解析2.4 loadBeanDefinitions - 具体的BeanDefinition加载逻辑2.5 load…