MoviePy的VideoClip开始和结束时间不起作用

0 投票
1 回答
14 浏览
提问于 2025-04-13 14:00

我正在尝试给一个视频添加字幕。我的目标是让每个单词在说出来的确切时刻显示出来。

我有一种方法可以准确获取每个单词的开始和结束时间:

def get_words_per_time(audio_speech_file):
    model = whisper.load_model("base")
    transcribe = model.transcribe(
        audio=audio_speech_file, fp16=False, word_timestamps=True
    )
    segments = transcribe["segments"]
    words = []

    for seg in segments:
        for word in seg["words"]:
            words.append(
                {
                    "word": word["word"],
                    "start": word["start"],
                    "end": word["end"],
                    "prob": round(word["probability"], 4),
                }
            )
    return words

然后我有一段代码,使用MoviePy来创建文本片段,并为每对单词分配一个开始和结束时间(我知道有些地方重复了,抱歉):

def generate_captions(
    words,
    font="Komika",
    fontsize=32,
    color="White",
    align="center",
    stroke_width=3,
    stroke_color="black",
):
    text_comp = []
    for i in track(range(0, len(words), 2), description="Creating captions..."):
        word1 = words[i]
        if i + 1 < len(words):
            word2 = words[i + 1]
        text_clip = TextClip(
            f"{word1['word']} {word2['word'] if i + 1 < len(words) else ''}",
            font=font,  # Change Font if not found
            fontsize=fontsize,
            color=color,
            align=align,
            method="caption",
            size=(660, None),
            stroke_width=stroke_width,
            stroke_color=stroke_color,
        )
        text_clip = text_clip.set_start(word1["start"])
        text_clip = text_clip.set_end(
            word2["end"] if i + 1 < len(words) else word1["end"]
        )
        text_comp.append(text_clip)
    return text_comp

最后,我把这些单词合并成一个完整的视频:

vid_clip = CompositeVideoClip(
    [vid_clip, concatenate_videoclips(text_comp).set_position(("center", 860))]
)

输出结果是这样的,但你可以明显看到这些单词和语音并没有同步。它们的移动速度似乎更快,好像开始和结束时间并没有起到作用。这里有一个视频

每个单词及其对应的开始/结束时间,看起来是这样的:

[
    {
        'word': 'This',
        'start': 0.0,
        'end': 0.22,
        'prob': 0.805
    },
    {
        'word': 'is',
        'start': 0.22,
        'end': 0.42,
        'prob': 0.9991
    },
    {
        'word': 'a',
        'start': 0.42,
        'end': 0.6,
        'prob': 0.999
    },
    {
        'word': 'test,
        ',
        'start': 0.6,
        'end': 1.04,
        'prob': 0.9939
    },
    {
        'word': 'to',
        'start': 1.18,
        'end': 1.3,
        'prob': 0.9847
    },
    {
        'word': 'show',
        'start': 1.3,
        'end': 1.54,
        'prob': 0.9971
    },
    {
        'word': 'words',
        'start': 1.54,
        'end': 1.9,
        'prob': 0.995
    },
    {
        'word': 'does',
        'start': 1.9,
        'end': 2.16,
        'prob': 0.997
    },
    {
        'word': 'not',
        'start': 2.16,
        'end': 2.4,
        'prob': 0.9978
    },
    {
        'word': 'appear.',
        'start': 2.4,
        'end': 2.82,
        'prob': 0.9984
    },
    {
        'word': 'At',
        'start': 3.46,
        'end': 3.6,
        'prob': 0.9793
    },
    {
        'word': 'their',
        'start': 3.6,
        'end': 3.8,
        'prob': 0.9984
    },
    {
        'word': 'proper',
        'start': 3.8,
        'end': 4.22,
        'prob': 0.9976
    },
    {
        'word': 'time.',
        'start': 4.22,
        'end': 4.72,
        'prob': 0.999
    },
    {
        'word': 'Thanks',
        'start': 5.04,
        'end': 5.4,
        'prob': 0.9662
    },
    {
        'word': 'for,
        ',
        'start': 5.4,
        'end': 5.66,
        'prob': 0.9941
    },
    {
        'word': 'watching.',
        'start': 5.94,
        'end': 6.36,
        'prob': 0.7701
    }
]

这可能是什么原因造成的呢?

1 个回答

0

我找到的简单解决办法是给每个单词设置一个持续时间,这个时间就是结束时间减去开始时间,单位是秒。然后,对于每次说话的停顿,我会添加一个只包含一个字母的文本片段,字体大小设置为1,这样它就看不见了。这样一来,字幕就能在应该出现的时间出现。我知道这不是最传统的解决方法,但它确实有效。

def generate_captions(
    words,
    font="Komika",
    fontsize=32,
    color="White",
    align="center",
    stroke_width=3,
    stroke_color="black",
):
    text_comp = []
    prev_end = 0

    blank_space = TextClip(
        "i",
        font=font,  # Change Font if not found
        fontsize=1,
        color="black",
        align="East",
        method="caption",
        size=(660, None),
        stroke_width=0,
    )
    for i in track(range(0, len(words), 2), description="Creating captions..."):
        word1 = words[i]
        word2 = word1
        if i + 1 < len(words):
            word2 = words[i + 1]
        text_clip = TextClip(
            f"{word1['word']} {word2['word'] if i + 1 < len(words) else ''}",
            font=font,  # Change Font if not found
            fontsize=fontsize,
            color=color,
            align=align,
            method="caption",
            size=(660, None),
            stroke_width=stroke_width,
            stroke_color=stroke_color,
        )
        text_clip = text_clip.set_duration(word2["end"] - word1["start"])
        if prev_end != word1["start"]:
            blank_space = blank_space.set_duration(word1["start"] - prev_end)
            text_comp.append(blank_space)
        prev_end = word2["end"]
        text_comp.append(text_clip)
    return text_comp

撰写回答