影片自动加上字幕
这篇文章会介绍使用 Python 的 Pillow 函数库,将外部字幕文件转换成字卡,搭配 moviepy 函数库,将字幕字卡与影片进行合成,实作影片自动加上字幕的效果。
快速导览:
本篇使用的 Python 版本为 3.7.12,所有范例可使用 Google Colab 实作,不用安装任何软件 ( 参考:使用 Google Colab )
安装 moviepy 和 Pillow
输入下列指令安装 moviepy,根据个人环境使用 pip 或 pip3。
!pip install moviepy
输入下列指令安装 Pillow,根据个人环境使用 pip 或 pip3,如果使用 Colab 或 Anaconda Jupyter,已经内建 Pillow 函数库。
!pip install Pillow
由于影片转档会使用 ffmpeg,因此也要安装 ffmpeg ( 影片存档常见错误“TypeError: must be real number, not NoneType”往往都是 ffmpeg 没有安装导致 ),根据个人环境使用 pip 或 pip3,Anaconda Jupyter 可以使用 conda install。
!pip install ffmpeg
读取字幕,转换成组合用的串列
延伸“下载 Youtube 影片 ( mp4、mp3、字幕 )”文章,下载影片与字幕档 ( 范例使用 baby shark ),下载后打开字幕文件,观察字幕文件的构成方式:
撰写下方的程序,将字幕文件的内容,根据字串拆分的规则,转换成“时间串列”和“字幕文字串列”,其中时间串列需要将“时分秒”转换成“总秒数”,详细说明标注在程序码中:
import os
os.chdir('/content/drive/MyDrive/Colab Notebooks') # 使用 Colab 要換路徑使用
# 定義轉換為總秒數的函式
def time2sec(t):
arr = t.split(' --> ') # 根據「' --> '」拆分文字
s1 = arr[0].split(',') # 前方的文字為開始時間
s2 = arr[1].split(',') # 後方的文字為結束時間
# 計算開始時間的總秒數
start = int(s1[0].split(':')[0])*3600 + int(s1[0].split(':')[1])*60 + int(s1[0].split(':')[2]) + float(s1[1])*0.001
# 計算結束時間的總秒數
end = int(s2[0].split(':')[0])*3600 + int(s2[0].split(':')[1])*60 + int(s2[0].split(':')[2]) + float(s2[1])*0.001
return [start, end] # 回傳開始時間與結束時間的串列
f = open('oxxostudio.srt','r') # 使用 open 方法的 r 開啟字幕檔案
srt = f.read() # 讀取字幕檔案內容
f.close() # 關閉檔案
srt_list = srt.split('\n') # 將內容根據換行符號 \n 拆分成串列
sec = 1 # 串列中秒數從第二項開始 ( 串列的第二項的索引值為 1 )
text = 2 # 串列中文字內容從第三項開始 ( 串列的第三項的索引值為 2 )
sec_list = [[0,0]] # 定義時間串列的開頭為 [0,0]
text_list = [''] # 定義字幕內容串列的開頭為空字串 ''
# 使用迴圈,讀取字幕檔案串列的每個項目
for i in range(len(srt_list)):
if i == sec:
sec = sec + 4 # 如果遇到時間內容,就將 sec + 4 ( 因為時間每隔 4 個項目會出現 )
# 如果兩個串列項目內容前後對不上 ( 前一個結束時間不等於後一個的開始時間 )
if sec_list[-1][1] != time2sec(srt_list[i])[0]:
# 在時間串列中間添加一個開始時間與結束時間內容 ( 表示該區間沒有字幕 )
sec_list.append([sec_list[-1][1],time2sec(srt_list[i])[0]])
# 在文字串列中間添加一個空字串 ( 表示該區間沒有字幕 )
text_list.append('')
sec_list.append(time2sec(srt_list[i])) # 添加時間到時間串列
if i == text:
text = text + 4 # 如果遇到文字內容,就將 text + 4 ( 因為文字每隔 4 個項目會出現 )
text_list.append(srt_list[i]) # 添加文字到文字串列
print(sec_list)
print(text_list)
合并影片与字幕
参考“影片中加入文字”文章,将字幕串列与时间串列组合,产生文字字卡,并根据时间串列切割原始影片,将影片片段与字卡组合,就能输出为带有字幕的影片了,详细说明写在程序码中:
import os
os.chdir('/content/drive/MyDrive/Colab Notebooks') # 使用 Colab 要換路徑使用
from moviepy.editor import *
from PIL import Image, ImageFont, ImageDraw
def time2sec(t):
arr = t.split(' --> ')
s1 = arr[0].split(',')
s2 = arr[1].split(',')
start = int(s1[0].split(':')[0])*3600 + int(s1[0].split(':')[1])*60 + int(s1[0].split(':')[2]) + float(s1[1])*0.001
end = int(s2[0].split(':')[0])*3600 + int(s2[0].split(':')[1])*60 + int(s2[0].split(':')[2]) + float(s2[1])*0.001
return [start, end]
f = open('oxxostudio.srt','r')
srt = f.read()
f.close()
srt_list = srt.split('\n')
#print(text_list)
sec = 1
text = 2
srt_list = [[0,0]]
text_list = ['']
for i in range(len(srt_list)):
if i == sec:
sec = sec + 4
if sec_list[-1][1] != time2sec(srt_list[i])[0]:
sec_list.append([sec_list[-1][1],time2sec(srt_list[i])[0]])
text_list.append('')
sec_list.append(time2sec(srt_list[i]))
if i == text:
text = text + 4
text_list.append(srt_list[i])
print(sec_list)
print(text_list)
img_empty = Image.new('RGBA', (480, 240)) # 產生 RGBA 空圖片
font = ImageFont.truetype('NotoSansTC-Regular.otf', 20) # 設定文字字體和大小
video = VideoFileClip("baby_shark.mp4").resize((480,240)) # 讀取影片,改變尺寸
video_duration = float(video.duration) # 讀取影片總長度
output_list = [] # 記錄最後要組合的影片片段
# 如果字幕最後的時間小於總長度
if sec_list[-1][1] != video_duration:
sec_list.append([sec_list[-1][1],video_duration]) # 添加時間到時間串列
text_list.append('') # 添加空字串到文字串列
# 建立文字字卡函式
def text_clip(text, name):
img = img_empty.copy() # 複製空圖片
draw = ImageDraw.Draw(img) # 建立繪圖物件,並寫入文字
text_width = 21*len(text) # 在 480x240 文字大小 20 狀態下,一個中文字長度約 21px
draw.text(((480-text_width)/2,10), text, fill=(255,255,255), font=font, stroke_width=2, stroke_fill='black')
img.save(name) # 儲存
# 建立影片和文字合併的函式
def text_in_video(t, text_img):
clip = video.subclip(t[0],t[1]) # 剪輯影片到指定長度
text = ImageClip(text_img, transparent=True).set_duration(t[1]-t[0]) # 讀取字卡,調整為影片長度
combine_clip = CompositeVideoClip([clip, text]) # 合併影片和文字
output_list.append(combine_clip) # 添加到影片片段裡
# 使用 for 迴圈,產生文字字卡
for i in range(len(text_list)):
text_clip(text_list[i], 'srt.png')
text_in_video(sec_list[i], 'srt.png')
output = concatenate_videoclips(output_list) # 合併所有影片片段
output.write_videofile("output.mp4",temp_audiofile="temp-audio.m4a", remove_temp=True, codec="libx264", audio_codec="aac")
print('ok')
执行程序后,就会产生一个 480x240 带有字幕的影片 ( 下图的影片截图上方,有中文字幕 )。
微信扫码关注
抖音扫码关注