给视频增加字幕,根据视频宽度和字幕的长度对比,判断是否要换行
单个字幕
import os
from moviepy.editor import VideoFileClip, CompositeVideoClip, ImageClip
from PIL import Image, ImageFont, ImageDraw
import numpy as np
def wrap_text(text, font, max_width):
"""
自动换行函数:根据指定的最大宽度对文本进行折行
:param text: 字幕文本
:param font: PIL字体对象
:param max_width: 每行最大宽度
:return: 换行后的文本列表
"""
lines = []
current_line = ""
for char in text: # 中文按字符处理
test_line = current_line + char
line_width = font.getbbox(test_line)[2] # 使用 getbbox(文本边界) 获取宽度
if line_width <= max_width:
current_line = test_line
else:
lines.append(current_line)
current_line = char
if current_line: # 添加最后一行
lines.append(current_line)
return lines
def generate_subtitle_image(text, font_path, font_size, video_width, video_height, white_space):
"""
生成带有字幕的图像,自动换行
:param text: 字幕文本
:param font_path: 字体路径
:param font_size: 字体大小
:param video_width: 视频宽度
:param video_height: 视频高度
:param white_space: 视频左右留白
:return: 带有字幕的图像(numpy数组)
"""
font = ImageFont.truetype(font_path, font_size)
max_width = video_width - white_space # 留左右边距
# 自动换行
wrapped_text = wrap_text(text, font, max_width)
# 创建字幕图像
image = Image.new("RGBA", (video_width, video_height), (255, 255, 255, 0)) # 透明背景
draw = ImageDraw.Draw(image)
# 绘制字幕:居中显示,每行水平居中,底部显示
line_height = font.getbbox("测试")[3] # 使用 getbbox 获取行高
y_position = video_height - line_height * len(wrapped_text) - 20 # 字幕距底部20像素
for line in wrapped_text:
text_width = font.getbbox(line)[2]
x_position = (video_width - text_width) // 2 # 居中
draw.text((x_position, y_position), line, font=font, fill="white")
y_position += line_height
return np.array(image)
def output(subtitle_image):
# 创建字幕ImageClip
subtitle_clip = ImageClip(subtitle_image, duration=video_clip.duration)
# 合成视频
composite_clip = CompositeVideoClip([video_clip, subtitle_clip])
# 输出视频
composite_clip.write_videofile(output_path, codec="libx264", fps=24)
if __name__ == '__main__':
# 视频路径
video_path = "1127.mp4"
output_path = "output_with_112750长.mp4"
# 加载视频并获取宽高
video_clip = VideoFileClip(video_path)
video_width, video_height = video_clip.w, video_clip.h
print(f"视频宽度: {video_width}px, 视频高度: {video_height}px")
# 字幕文本
subtitle_text = "sdakjslfkdfgdfsfagfasfgsagf"
print(f"字幕原始文本长度: {len(subtitle_text)}")
# 字幕样式
custom_font_path = r"C:\Windows\Fonts\msyh.ttc" # 替换为实际字体路径
if not os.path.exists(custom_font_path):
print(f"字体文件不存在:{custom_font_path}")
else:
print(f"字体文件存在:{custom_font_path}")
font_size = 50
white_space = 20 #字幕左右留白
# 生成字幕图像
subtitle_image = generate_subtitle_image(
subtitle_text, custom_font_path, font_size, video_width, video_height, white_space
)
output(subtitle_image)
多段字幕
from moviepy.editor import VideoFileClip, CompositeVideoClip, ImageClip
from PIL import Image, ImageFont, ImageDraw
import numpy as np
# 自动换行函数
def wrap_text(text, font, max_width):
lines = []
current_line = ""
for char in text:
test_line = current_line + char
line_width = font.getbbox(test_line)[2]
if line_width <= max_width:
current_line = test_line
else:
if current_line:
lines.append(current_line)
current_line = char
if current_line:
lines.append(current_line)
return lines
# 计算每段字幕的宽高,并生成图像
def generate_subtitle_image(text, font_path, font_size, video_width, line_spacing=10):
font = ImageFont.truetype(font_path, font_size)
max_width = video_width - 40 # Subtract some padding
wrapped_text = wrap_text(text, font, max_width)
# 计算每行的高度,基于行数和行距来计算总高度
line_height = font.getbbox("测试")[3]
image_height = line_height * len(wrapped_text) + line_spacing * (len(wrapped_text) - 1) + 20 # 额外的顶部和底部填充
image = Image.new("RGBA", (video_width, image_height), (255, 255, 255, 0))
draw = ImageDraw.Draw(image)
y_position = 10
for line in wrapped_text:
text_width = font.getbbox(line)[2]
x_position = (video_width - text_width) // 2 # 居中显示
draw.text((x_position, y_position), line, font=font, fill="white")
y_position += line_height + line_spacing
return np.array(image), image_height
# 动态调整字体大小(如果需要)
def adjust_font_size(text, font_path, max_width, max_height, initial_font_size=40):
font_size = initial_font_size
font = ImageFont.truetype(font_path, font_size)
# 不断减小字体,直到文本适配屏幕宽度和高度
while True:
wrapped_text = wrap_text(text, font, max_width)
line_height = font.getbbox("测试")[3]
total_height = line_height * len(wrapped_text) + (len(wrapped_text) - 1) * 10 # 行高和行间距
if total_height <= max_height and all(font.getbbox(line)[2] <= max_width for line in wrapped_text):
break
font_size -= 1 # 字体过大时减小字体
font = ImageFont.truetype(font_path, font_size)
return font, wrapped_text, line_height
def output(subtitle_clips):
# 合成视频
composite_clip = CompositeVideoClip([video_clip] + subtitle_clips)
composite_clip.write_videofile(output_path, codec="libx264", fps=24)
def letter_generation(subtitles):
# 生成字幕片段
subtitle_clips = []
for subtitle in subtitles:
# 调整字体大小以适配视频宽度和高度
font, wrapped_text, line_height = adjust_font_size(
subtitle["text"], custom_font_path, video_width - 40, video_height // 4, initial_font_size
)
# 生成字幕图像
subtitle_image, subtitle_height = generate_subtitle_image(
subtitle["text"], custom_font_path, font.size, video_width, line_spacing
)
# 设置字幕位置并考虑底部留白(底部空隙 20 像素)
subtitle_clip = (
ImageClip(subtitle_image, duration=subtitle["end"] - subtitle["start"])
.set_start(subtitle["start"])
.set_position(("center", video_height - subtitle_height - 30)) # 给字幕底部增加 30 像素空隙
)
subtitle_clips.append(subtitle_clip)
output(subtitle_clips)
if __name__ == '__main__':
# 主逻辑
video_path = "1127.mp4"
output_path = "output_with_subtitles1.mp4"
# 视频片段
video_clip = VideoFileClip(video_path)
video_width, video_height = video_clip.w, video_clip.h
# 字体配置
custom_font_path = r"C:\Windows\Fonts\msyh.ttc" # 替换为实际字体路径
initial_font_size = 40
line_spacing = 10 # 设置行距
# 字幕列表,每段字幕包含文本、开始时间、结束时间
subtitles = [
{"text": "这是第一段字幕,显示时间是0到5秒adfa", "start": 0, "end": 5},
{"text": "这是第二段字幕,显示时间是5到10秒,这段字幕会自动换行af。", "start": 5, "end": 10},
{"text": "第三段字幕更短,显示时间是10到15秒asda。", "start": 10, "end": 15},
]
letter_generation(subtitles)
标签:moviepy,text,width,字幕,添加,import,line,font
From: https://www.cnblogs.com/msmsga/p/18572764