【脚本工具】视频抽帧、添加srt字幕朗读、添加背景音频

news2025/1/22 8:29:05

1.文章目录

看完本文章,你将能学会一下内容:

  1. 批量视频抽帧;
  2. 添加srt字幕;
  3. 添加srt配音;
  4. 添加背景音乐;
  5. 多视频片段合成一个新视频;

效果:

2.安装依赖

首先安装视频处理库opencv-pythonmoviepy,安装方式pip install xxx。

py文件顶部中导入(下文中使用到的函数将不在赘述导入过程):

from moviepy.editor import VideoFileClip, CompositeAudioClip, CompositeVideoClip, concatenate_videoclips
from cv2 import VideoCapture, VideoWriter, VideoWriter_fourcc, CAP_PROP_FRAME_COUNT

2.视频抽帧

cv2.VideoCapture:

作用:用于从视频文件或摄像头捕获视频帧。

解释:这个函数允许你创建一个视频捕获对象,以便从指定的视频源中读取视频帧。你可以传递视频文件的路径或摄像头的索引作为参数,然后使用它来逐帧读取视频。

cv2.VideoWriter:

作用:用于将视频帧写入到一个新的视频文件中。

解释:这个函数允许你创建一个视频写入对象,以便将视频帧写入到一个新的视频文件中。你可以指定输出文件的名称、视频编解码器、帧速率、分辨率等参数。这在视频编辑和保存处理后的视频时非常有用。

cv2.VideoWriter_fourcc:

作用:用于指定视频编解码器的四字符码(FourCC)。

解释:FourCC 是一个4字节的代码,用于标识视频编解码器。不同的编解码器有不同的FourCC码。通过使用这个函数,你可以选择要在输出视频中使用的编解码器。

CAP_PROP_FRAME_COUNT:

作用:用于获取视频文件中的总帧数。

解释:CAP_PROP_FRAME_COUNT 是 cv2.VideoCapture 对象的一个属性,用于获取视频文件中的总帧数。这对于确定视频的时长以及循环播放视频等操作非常有用。

完整代码块:

# 单个视频抽帧
def video_extract_frame(video_path, out_path):

  # 打开视频文件
  vc = VideoCapture(video_path)

  # 视频的总帧数
  total_frame = int(vc.get(CAP_PROP_FRAME_COUNT))

  video = VideoFileClip(video_path, audio=False)

  # # 创建一个视频写入对象。设置视频的宽度和高度、帧率、输出路径
  fourcc = VideoWriter_fourcc(*'mp4v')
  videoWriter = VideoWriter(out_path, fourcc, VIDEO_FPS, (video.w, video.h))

  if vc.isOpened():
      status, frame = vc.read()
  else:
      status = False
      print("视频没有打开成功!")
      vc.release()
      video.close()
      videoWriter.release()
      return False

  if status:
      for index in trange(total_frame, desc='抽帧进度'):
          # 读取视频帧并写入输出视频
          status, frame = vc.read()
          if index % VIDEO_FPS == 0:
              skip_frames = tool.get_unique_random_numbers(index, VIDEO_FPS)
          if index in skip_frames:
              continue
          videoWriter.write(frame)

  videoWriter.release()
  vc.release()
  video.close()

调用上面的抽帧函数:

video_extract_frame('./test.mp4', 'result.mp4')

如果有多个视频需要抽帧,只需要循环调用即可:


 for index, video_file in enumerate(['1.mp4', '2.mp4', '3.mp4']):
    
    //取出链接中的视频文件名称,例如1,2,3
    video_name = tool.get_file_name(video_file)
    //组装成新目录、新名字(根据自己需要)
    out_path = "{}{}.mp4".format(frame_path, video_name)

    videoTool.video_extract_frame(video_file, out_path)
    print("\n第{}条, 视频抽帧已完成: {}".format(index + 1, out_path))

3.添加srt字幕;

完整示例代码:


# 字幕片段
def generate_textclip(text, width, params, start, duration) -> TextClip:

    return TextClip(
        text,
        font=params.get('font'),
        align=params.get('align'),
        fontsize=params.get('fontsize'),
        color=params.get('color', '#ffbd00'),
        size=(width, params.get('height')),
        stroke_color=params.get('stroke_color'),
        stroke_width=params.get('stroke_width')
    ).set_position((params['location']['x'], params['location']['y'])).set_duration(duration).set_start(start)


# 添加srt字幕
def add_srt(video_clip, params):

  if not (isfile(params["srt_path"]) and params["srt_path"].endswith('.srt')):
      print('字幕仅支持srt格式!')
      return []
  else:
      # 获取视频的宽度和高度
      v_width, v_height = video_clip.w, video_clip.h
      # 所有字幕剪辑
      txts = []
      content = tool.read_srt(params["srt_path"])
      sequences = tool.get_sequences(content)

      max_count = len(sequences)

      max_duration = video_clip.duration

      srt_text = params["srt_text"]

      for index, line in enumerate(sequences):
          print("字幕生成进度:{}/{}".format(index, max_count))
          if len(line) < 3:
              continue
          start = line[1].split(' --> ')[0]
          end = line[1].split(' --> ')[1]

          start = tool.str_float_time(start)
          end = tool.str_float_time(end)

          start, end = map(float, (start, end))

          if start >= max_duration:
              break
          if end >= max_duration:
              end = max_duration

          duration = end - start

          txt_srt = generate_textclip(line[2], (v_width - 20), srt_text, start, duration)

          txts.append(txt_srt)

      print("\n字幕转换已完成...")

      return txts

tool.read_srt 代码:

def read_srt(self, path):
    content = ""
    with open(path, 'r', encoding='UTF-8') as f:
        content = f.read()
        return content

tool.get_sequences 代码:

# 字幕拆分
def get_sequences(self, content):
    sequences = content.split('\n\n')
    sequences = [sequence.split('\n') for sequence in sequences]
    # 去除每一句空值
    sequences = [list(filter(None, sequence)) for sequence in sequences]
    # 去除整体空值
    return list(filter(None, sequences))

tool.str_float_time 代码:

# 字符串数字格式化成时间
def str_float_time(self, str):
    str_list = str.split(':')
    hour = int(str_list[0])
    minute = int(str_list[1])
    second = int(str_list[2].split(',')[0])
    minsecond = int(str_list[2].split(',')[1])
    allTime = hour * 60 * 60 + minute * 60 + second + minsecond / 1000
    return allTime

4.添加背景音乐、srt音频、导出

# frame_video_paths:是抽帧后的视频片段
def merge_videos_with_bgm(params, frame_video_paths, out_path):
    # 用VideoFileClip加载所有输入视频文件
    video_clips = [VideoFileClip(file) for file in frame_video_paths]

    final_clip = concatenate_videoclips(video_clips)

    bgm_music = audioTool.get_audio({
        "volume": params.get("bgm_volume", 0.1), #背景音频小声点
        "duration": final_clip.duration,
        "audio_path": params['bgm_path']
    })

    # 加载主音乐(朗读音频)
    main_music = audioTool.get_audio({
        "volume": params.get("audio_volume", 1.0),# 朗读音频大声点
        "duration": final_clip.duration,
        "audio_path": params['audio_path']
    })


    # 将音乐混合到最终视频切片
    final_clip = final_clip.set_audio(CompositeAudioClip([main_music, bgm_music]))

    video_srt = videoTextTool.add_srt(final_clip, params)


    final_clip.extend(video_srt)

    # 复合视频
    video = CompositeVideoClip(final_clip)

    output_path = path.join(out_path, "result.mp4")

    # 输出最终视频
    video.write_videofile(output_path)


更多爬虫精彩案例、工具源码,搜索公众号“一个努力奔跑的snail
实战完整源码,已上传微信公众号,后期分享更精彩!

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

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

相关文章

ERR invalid password

E:\Document_Redis_Windows\redis-2.4.5-win32-win64\64bit redis.conf

前端实现调用打印机和小票打印(TSPL )功能

Ⅰ- 壹 - 使用需求 前端 的方式 点击这个按钮&#xff0c;直接让打印机打印我想要的东西 Ⅱ - 贰 - 小票打印 目前比较好的方式就是直接用 TSPL 标签打印指令集, 基础环境就不多说了,这个功能的实现就是利用usb发送指令,现在缺少个来让我们能够和usb沟通的工具,下面这就是推…

代码随想录 Day35 动态规划04 01背包问题和完全背包问题 LeetCode T416 分割等和子集

背包问题 说到背包问题大家都会想到使用动规的方式来求解,那么为什么用动规呢,dp数组代表什么呢?初始化是什么,遍历方式又是什么,这篇文章笔者将详细讲解背包问题的经典例题0-1背包问题和完全背包问题的解题方式,希望能帮助到大家 1.暴力方式 有人一提到背包问题就只会使用动态…

C++之队列queue

1.知识百科 队列是一种特殊的线性表&#xff0c;特殊之处在于它只允许在表的前端&#xff08;front&#xff09;进行删除操作&#xff0c;而在表的后端&#xff08;rear&#xff09;进行插入操作&#xff0c;和栈一样&#xff0c;队列是一种操作受限制的线性表。进行插入操作的…

Idea - Apifox Helper 插件的安装、配置令牌、导出

第一步&#xff1a;先安装插件&#xff08;其他EASY API 、Api docx同理&#xff09; 等待安装完毕 第二步&#xff1a; 导出你想导出的API 提示我们没有找到配置文件&#xff0c;需要到设置里面设置Personal Access Token 第三步&#xff1a;到设置里面设置Personal Access T…

AI:50-基于深度学习的柑橘类水果分类

🚀 本文选自专栏:AI领域专栏 从基础到实践,深入了解算法、案例和最新趋势。无论你是初学者还是经验丰富的数据科学家,通过案例和项目实践,掌握核心概念和实用技能。每篇案例都包含代码实例,详细讲解供大家学习。 📌📌📌本专栏包含以下学习方向: 机器学习、深度学…

城市内涝解决方案:实时监测,提前预警,让城市更安全

城市内涝积水问题是指城市地区在短时间内遭遇强降雨后&#xff0c;地面积水过多&#xff0c;导致城市交通堵塞、居民生活不便、财产损失等问题。近年来&#xff0c;随着全球气候变化和城市化进程的加速&#xff0c;城市内涝积水问题越来越突出&#xff0c;成为城市发展中的一大…

C++之初始化列表详细剖析

一、初始化列表定义 初始化列表&#xff1a;以一个冒号开始&#xff0c;接着是一个以逗号分隔的数据成员列表&#xff0c;每个"成员变量"后面跟一个放在括号中的初始值或表达式。 class Date { public:Date(int year, int month, int day): _year(year), _month(mont…

tauri 访问静态资源,响应头为Content-Type:‘text/html‘

现象 报错&#xff1a; Error: THREE.FBXLoader: Unknown format. at FBXLoader.parse (index-f8291142.js:22050:15) at Object.onLoad (index-f8291142.js:22033:22) at index-f8291142.js:19769:20 使用threejs 加载fbx模型文件时&#xff0c;返回头中Content-…

《C++ Primer》第6章 函数(一)

参考资料&#xff1a; 《C Primer》第5版《C Primer 习题集》第5版 6.1 函数基础&#xff08;P182&#xff09; 典型的函数定义包括&#xff1a;返回类型&#xff08; return type &#xff09;、函数名字、0 个或多个形参&#xff08; parameter &#xff09;组成的列表、函…

使用HttpClient库的爬虫程序

使用HttpClient库的爬虫程序&#xff0c;该爬虫使用C#来抓取内容。 using System; using System.Net.Http; using System.Threading.Tasks; ​ namespace CrawlerProgram {class Program{static void Main(string[] args){// 创建HttpClient对象using (HttpClient client new…

数据结构 - 顺序表ArrayList

目录 实现一个通用的顺序表 总结 包装类 装箱 / 装包 和 拆箱 / 拆包 ArrayList 与 顺序表 ArrayList基础功能演示 add 和 addAll &#xff0c;添加元素功能 ArrayList的扩容机制 来看一下&#xff0c;下面的代码是否存在缺陷 模拟实现 ArrayList add 功能 add ind…

Linux多线程服务端编程:使用muduo C++网络库 学习笔记 第七章 muduo编程示例(上)

本章将介绍如何用muduo网络库完成常见的TCP网络编程任务。内容如下&#xff1a; 1.[UNP]中的五个简单协议&#xff0c;包括echo、daytime、time、discard、chargen等。 2.文件传输&#xff0c;示范非阻塞TCP网络程序中如何完整地发送数据。 3.Boost.Asio中的示例&#xff0c;…

新手学习两种不同的存储方式(不喜勿喷)

localStorage.setItem(sid,res.info.id)console.log(sid,res.info.id)sessionStorage.setItem(uid,res.info.id) sid和uid&#xff0c;也可以写其他的只来替代&#xff1b;而res.info.id是接口的数据可替换成自己的 一次性传的id值刷新页面就没有 永久性传的id 这是Javascript…

Linux——切换CUDA版本

一、查看本地cuda版本 cd /usr/local/ ls当前cuda为软连接&#xff0c;指向指定的cuda版本 stat cuda # 查看当前cuda状态信息二、切换CUDA版本 # 删除原有软连接 sudo rm -rf /usr/local/cuda # 建立需要切换的cuda软连接版本 sudo ln -s /usr/local/cuda-**.* /usr/l…

【GitLab、GitLab Runner、Docker】GitLab CI/CD 应用

安装Gitlab开源版 官方文档-安装Gitlab 使用Docker安装 sudo docker run --detach \--hostname gitlab.example.com \--env GITLAB_OMNIBUS_CONFIG"external_url http://${ip}:9999/; gitlab_rails[gitlab_shell_ssh_port] 8822;" \--publish 443:443 --publish 99…

Circos图绘制

Circos图其实是一个用途非常广泛的图形&#xff0c;可以用于表征基本上任何类型的数据&#xff0c;包括把我们常见的散点图、折线图和柱状图等都可以整合到Circos当中。特别是&#xff0c;Circos尤其适合用来描述生物信息学和基因组学的数据。 1.绘制Circos图 目前绘制Circos图…

单元测试一(理论)-云计算2023.11-云南农业大学

实验《 ECS数据管理实践-备份与恢复》 https://developer.aliyun.com/adc/scenario/7ad13e326c924d07a1ddb4e88cb26ce4 数据盘挂载到 /alidata&#xff0c;alidata改为自己姓名全拼&#xff0c;数据盘上创建文件test.txt&#xff0c;改为test接自己姓名全拼.txt&#xff0c;快…

根据Word模板,使用POI生成文档

突然想起来有个小作业&#xff1a;需要根据提供的Word模板填充数据。这里使用POI写了一个小demo验证下。 测试用模板&#xff1a; 执行结果 1.引入依赖坐标 <dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml</artifactId&…

07、如何取出对象数组里的固定 key 值来拼成一个对象(以及对象与数组之间的转化)

1、数据情况&#xff1a; 其一、从后端拿到的数据为&#xff1a; let arr [1, 3, 7, 24] 其二、目标数据为(即&#xff1a;后台需要下发的数据)&#xff1a; {vlan_1: 1, vlan_3: 1, vlan_7: 1, vlan_24: 1} 2、操作过程&#xff1a; 其一、定义一个动态的 key 值来满足…