Pixelle-Video 源码解析 #11:AI 配图生成:每句话如何匹配一张画面?

前面第 8 篇我们分析了 Pixelle-Video 的分镜规划逻辑,重点讲了:

主题 / 固定脚本
    ↓
narrations
    ↓
image_prompts
    ↓
StoryboardFrame

第 9 篇又分析了 Prompt 设计,说明 Pixelle-Video 如何通过 prompt 控制文案、标题和视觉风格。

这一篇继续往下看:

AI 配图生成。

也就是每一句旁白,最终是如何匹配到一张画面的。

很多人以为 Pixelle-Video 的配图流程是:

旁白 → 直接拿旁白去生成图片

但源码里的真实流程要更细一些:

旁白 narration
    ↓
LLM 生成英文 image_prompt
    ↓
应用 prompt_prefix 统一画风
    ↓
创建 StoryboardFrame
    ↓
FrameProcessor 逐帧处理
    ↓
MediaService 调用 ComfyUI / RunningHub / API 模型
    ↓
下载生成结果
    ↓
保存到 frame.image_path
    ↓
HTML 模板把图片和字幕合成画面
    ↓
生成单段视频

所以,Pixelle-Video 的 AI 配图不是单纯“给每句话配一张图”,而是一个完整的工程链路。

这一篇我们就沿着这条链路,把它拆开来看。

一、AI 配图在完整流程中的位置

StandardPipeline 的注释中,Pixelle-Video 的标准流程包括:生成或拆分 narrations、为每段 narration 生成 image prompts、逐帧生成音频、图片、模板画面和视频片段,最后拼接视频并添加 BGM。源码注释把“Generate image prompts for each narration”和“For each frame: Generate image”明确列在标准流程里。

这说明 AI 配图不是独立功能,而是夹在两个阶段中间:

前面:
文案生成 / 脚本拆分 / 分镜规划

中间:
AI 配图生成

后面:
TTS 配音 / 模板渲染 / 视频合成

它承担的是“把文字分镜变成视觉素材”的职责。

如果没有配图阶段,视频只能是纯文字或固定背景。
有了配图阶段,每段旁白就能拥有对应画面。

二、第一步:只有 image / video 模板才需要生成配图

Pixelle-Video 不是任何时候都生成图片。

StandardPipeline.plan_visuals() 中,源码会先读取 frame_template,再通过 get_template_type() 判断模板类型。如果模板类型是 image,说明需要图片生成;如果是 video,说明需要视频生成;如果是 static,就跳过媒体生成流程。

这一步非常关键。

因为模板类型直接决定后面要不要调用 LLM 生成 image prompt,以及要不要调用媒体模型生成图片。

可以理解成:

static 模板:
    不生成图片
    不生成 image_prompt
    只用文字 / 固定模板合成视频

image 模板:
    为每段 narration 生成 image_prompt
    再根据 image_prompt 生成图片

video 模板:
    也需要视觉 prompt
    后续生成视频素材,而不是静态图片

所以 AI 配图的第一个判断条件是:

当前模板是否需要媒体素材。

这也解释了为什么 Pixelle-Video 支持低成本快速生成。
如果用户选择 static 模板,它会直接跳过配图和媒体生成,不依赖 ComfyUI,也不消耗图片生成额度。

三、第二步:narration 不能直接等于 image_prompt

为什么 Pixelle-Video 不直接拿旁白去生成图片?

因为旁白是给人听的,图片 prompt 是给图像模型看的,两者不是同一种文本。

例如旁白是:

很多人越努力越焦虑,不是因为不够自律,而是目标太混乱。

这句话适合 TTS 朗读,但不适合直接生成图片。

图片模型更需要具体画面:

A tired young office worker sitting at a messy desk late at night, surrounded by sticky notes, unfinished plans and glowing laptop screens, anxious expression, cinematic lighting, realistic style

这就是 Pixelle-Video 要先生成 image_prompt 的原因。

从源码看,generate_image_prompts() 的输入是 narrations: List[str],输出是 List[str],也就是一组基础图片提示词。函数说明中也写明,它会根据 narrations 生成 image prompts,并且支持批量和重试。

所以配图生成的真实第一步不是“生图”,而是:

把旁白翻译成视觉描述

四、第三步:image_generation prompt 如何约束画面

图片提示词生成的 Prompt 在 pixelle_video/prompts/image_generation.py 中。

这个 Prompt 把模型设定为“专业视觉创意设计师”,任务是根据每段 narration 生成对应的英文 image prompt,并且要求每段画面都准确反映对应旁白的内容和情绪。源码中还明确要求输出必须是英文,因为图片生成模型通常更适合英文 prompt。

它主要约束了几件事。

第一,必须一一对应。

输入有多少段 narration,就必须输出多少个 image prompt。源码里直接强调:输入包含 {narrations_count} 段 narration,就必须生成同样数量的 image prompts。

第二,必须使用英文。

这不是为了给用户阅读,而是为了提高图像生成模型的理解效果。

第三,描述要具体。

Prompt 要求图片描述包含:

scene
character action
emotion
symbolic elements

也就是场景、人物动作、情绪和象征元素。

第四,要把抽象概念视觉化。

例如用道路代表人生选择,用锁链代表约束,用凌乱桌面代表混乱目标。

第五,图片要服务文案。

Prompt 明确要求图片应该成为文案内容的视觉延伸,避免无关或矛盾画面。

这套要求的目标很明确:

不是生成好看的随机图片,而是生成能增强旁白理解的配图。

五、第四步:批量生成 image_prompts,并校验数量

有了 Prompt 模板后,真正执行的是 generate_image_prompts()

源码里它会把 narrations 按 batch_size 分批,默认每批 10 条。每批先调用 build_image_prompt_prompt() 构造 prompt,然后调用 llm_service,再用 _parse_json() 解析结果。解析后会检查是否存在 image_prompts 字段。

更关键的是,它会检查:

返回的 image_prompts 数量 == 当前 batch 的 narrations 数量

如果数量不一致,就记录 warning,并在未达到最大重试次数时重试;如果最终仍然不一致,就抛出错误。源码里默认最大重试次数是 3。

这个设计非常重要。

因为 Pixelle-Video 后面要保证:

narration[0] → image_prompt[0]
narration[1] → image_prompt[1]
narration[2] → image_prompt[2]

如果输入 5 段 narration,模型只返回 4 个 image prompt,后面就会错位。

错位之后,视频会变得很奇怪:

旁白讲学习压力,画面却是晨跑
旁白讲目标拆分,画面却是办公室会议
旁白讲焦虑缓解,画面却是凌乱桌面

所以 Pixelle-Video 的配图逻辑里,最关键的不是“生成 prompt”,而是保证 一一对应关系

六、第五步:prompt_prefix 统一整条视频画风

generate_image_prompts() 生成的是基础 prompt。

基础 prompt 只负责“这一帧画什么”。
但短视频还需要统一画风。

所以 StandardPipeline.plan_visuals() 会在生成 base image prompts 后读取 prompt_prefix,再调用 build_image_prompt() 把 prefix 加到每个 base prompt 前面。

build_image_prompt() 的实现非常简单:如果 prefix 和 prompt 都存在,就返回 prefix, prompt;如果只有 prefix,就返回 prefix;否则返回原 prompt。

例如基础 prompt 是:

A tired student sitting at a messy desk late at night

风格前缀是:

black and white stick figure sketch, minimalist line art, pure white background

最终 prompt 就会变成:

black and white stick figure sketch, minimalist line art, pure white background, A tired student sitting at a messy desk late at night

这样每一帧内容不同,但整体风格一致。

这对短视频账号非常重要。
如果第一帧是写实摄影,第二帧是 3D 卡通,第三帧是水彩插画,第四帧又变成赛博朋克,整条视频会显得很散。

所以 Pixelle-Video 的配图逻辑分成两层:

base image_prompt:
    控制每一帧画什么

prompt_prefix:
    控制所有帧用什么画风

这也是它能做账号风格沉淀的关键。

七、第六步:image_prompt 被写入 StoryboardFrame

图片提示词生成完以后,Pixelle-Video 会进入 initialize_storyboard()

源码中会遍历:

zip(ctx.narrations, ctx.image_prompts)

然后为每一组 narration 和 image_prompt 创建一个 StoryboardFrame,并把它追加到 ctx.storyboard.frames 中。

StoryboardFrame 的字段包括:

index
narration
image_prompt
audio_path
media_type
image_path
video_path
composed_image_path
video_segment_path
duration

源码中的 dataclass 也明确把 narration 定义为旁白文本,把 image_prompt 定义为图片生成提示词,把 image_path 定义为原始图片路径。

也就是说,配图流程到这里还没有真正生成图片。
它只是把“每句话应该配什么画面”写进了 frame。

此时一个 frame 可能长这样:

StoryboardFrame 0
    narration:
        很多人越努力越焦虑,不是因为不够自律,而是目标太混乱。

    image_prompt:
        black and white stick figure sketch, A tired person surrounded by messy task lists...

    image_path:
        None

后面 FrameProcessor 会真正拿这个 image_prompt 去生成图片,并填充 image_path

八、第七步:FrameProcessor 判断这一帧是否需要生成媒体

真正生成图片发生在 FrameProcessor 中。

FrameProcessor.__call__() 会处理单个 frame,它的步骤包括:生成音频、生成媒体、合成画面、创建视频片段。源码注释里也明确列出这四步。

在进入媒体生成前,它会先判断:

has_existing_media = frame.image_path is not None or frame.video_path is not None
needs_generation = frame.image_prompt is not None

如果 needs_generation 为真,说明这一帧需要根据 prompt 生成图片或视频。
如果已经有 image_pathvideo_path,说明可能是 asset-based pipeline 传入的已有素材,就不再生成。
如果两者都没有,说明当前模板不需要媒体,直接跳过。

这个判断很实用。

它让 Pixelle-Video 同时支持三类情况:

AI 生成配图:
    image_prompt 不为空,需要生成

用户已有素材:
    image_path / video_path 已存在,直接复用

静态模板:
    image_prompt 为空,不需要媒体

所以 AI 配图只是 Pixelle-Video 媒体处理的一种路线,而不是唯一路线。

九、第八步:先生成音频,再生成图片

FrameProcessor 的处理顺序是:

1. 生成 TTS 音频
2. 生成媒体
3. 合成模板画面
4. 创建视频片段

源码里 _step_generate_audio() 会把 frame.narration 传给 TTS,并把生成音频路径保存到 frame.audio_path。随后它会读取音频时长,写入 frame.duration

为什么配图前要先生成音频?

对图片生成来说,顺序影响不大。
但对视频媒体生成来说,时长很重要。

源码中 _step_generate_media() 会判断当前是 image 还是 video。如果是 video workflow,并且 frame 已经有音频时长,就会把 duration 传给媒体生成服务,用 TTS 音频时长作为目标视频时长。

这说明 Pixelle-Video 的 frame 处理是“旁白驱动”的:

旁白决定音频
音频决定时长
时长影响视频素材
素材再进入模板和合成

即使本文重点是 AI 配图,也要注意:Pixelle-Video 的每一帧并不是只考虑图片,而是围绕 narration、audio、media、segment 一起处理。

十、第九步:MediaService 调用 ComfyUI / RunningHub / API 模型

_step_generate_media() 会构造 media_params,其中最重要的是:

prompt = frame.image_prompt
workflow = config.media_workflow
media_type = image 或 video
width = config.media_width
height = config.media_height
output_path = 当前 frame 的输出路径
index = frame.index + 1

然后调用:

media_result = await self.core.media(**media_params)

源码中正是这样把 frame.image_prompt 传给媒体生成服务。

MediaService 是基于 ComfyUI workflow 的媒体生成服务,使用 ComfyKit 执行图片或视频生成工作流。它会扫描 workflows 目录下以 image_video_ 开头的 JSON 工作流文件。

也就是说,图片生成这一步并不是写死某个模型。

它可能走:

本地 ComfyUI workflow
RunningHub 云端 workflow
api/... 直连媒体模型

MediaService.__call__() 里还有一个重要分支:如果选中的 workflow 以 api/ 开头,就转交给 APIProviderMediaService;否则就解析 workflow,并通过共享的 ComfyKit 实例执行本地或 RunningHub 工作流。

这让 Pixelle-Video 的配图生成路线非常灵活。

十一、ComfyUI / RunningHub 路线:workflow 决定怎么生图

如果不是 api/ workflow,MediaService 会解析 workflow,然后调用 ComfyKit 执行。

源码中会根据 workflow 来源决定传给 ComfyKit 的内容:如果是 RunningHub 并且有 workflow_id,就传 RunningHub workflow_id;否则传本地 workflow 文件路径。随后通过 kit.execute(workflow_input, workflow_params) 执行工作流。

这个设计说明:

Pixelle-Video 并不关心 ComfyUI workflow 内部用了什么模型。
它只负责传入参数:

prompt
width
height
duration
negative_prompt
steps
seed
cfg
sampler

真正的生图细节交给 workflow。

这对二次开发很友好。

你想换 FLUX、SDXL、Wan、其他图像模型,不一定要改 Python 主流程。
只要 workflow 接收 Pixelle-Video 传入的 promptwidthheight 等参数,就能接入。

十二、API 路线:直接调用云端图像模型

如果 workflow 是 api/...,MediaService 会转交给 APIProviderMediaService

APIProviderMediaService 把 API 模型包装成和 workflow 类似的形态。源码中 list_workflows() 会把图片模型和视频模型都转换成带有 api/provider/model key 的 workflow 信息。

当媒体类型是 image 时,它会调用 _generate_image(),创建 ImageClient,然后把 prompt、model、分辨率、比例等参数传给云端图像生成接口。最终返回一个 MediaResult(media_type="image", url=result_path)

这意味着 Pixelle-Video 的配图有两种主要扩展方式:

workflow 方式:
    适合 ComfyUI / RunningHub 用户,自由度高

API 方式:
    适合不想维护 ComfyUI 的用户,直接使用云端图像模型

从上层 pipeline 看,它们都是:

self.core.media(prompt=..., workflow=..., media_type="image")

区别被隐藏在 MediaService 后面。

十三、第十步:生成结果写回 frame.image_path

媒体生成完成后,_step_generate_media() 会根据返回结果判断是图片还是视频。

如果是图片,它会下载结果到本地任务目录,并把路径写入:

frame.image_path = local_path

如果是视频,则写入:

frame.video_path = local_path

源码中也会同步设置 frame.media_type = media_result.media_type

所以,配图真正完成的标志是:

frame.image_path 不再是 None

此时 frame 从“计划状态”变成“拥有真实图片素材”的状态:

生成前:
    narration
    image_prompt

生成后:
    narration
    image_prompt
    image_path
    media_type = image

后面的模板渲染和视频合成,就会使用这个 image_path

十四、第十一步:HTML 模板把图片和旁白合成画面

图片生成以后,还不是最终视频画面。

Pixelle-Video 还要把图片、标题、旁白、模板参数组合起来,生成合成画面。

FrameProcessor._step_compose_frame() 会调用 _compose_frame_html(),后者会解析模板路径,创建 HTMLFrameGenerator,再调用 generate_frame()。传入的参数包括:

title = storyboard.title
text = frame.narration
image = media_path
ext = 扩展参数
output_path = composed 输出路径

源码里 media_path 会根据 frame.media_type 判断使用 frame.video_path 还是 frame.image_path

也就是说,AI 生成的图片只是背景或素材。

最终画面还会经过模板处理:

AI 图片
    +
标题
    +
旁白字幕
    +
模板样式
    ↓
composed_image_path

这也是 Pixelle-Video 和普通“批量生成图片”工具的区别。

它不是只生成图片,而是把图片纳入短视频模板体系。

十五、第十二步:图片 + 音频变成视频片段

合成画面之后,FrameProcessor 会执行 _step_create_video_segment()

如果当前 frame 是 image 类型,通常就是把合成图片和音频结合,生成一个视频片段。
如果当前 frame 是 video 类型,则会把视频素材、模板叠加层和音频进行组合。源码中 _step_create_video_segment() 会根据 frame.media_type 分支处理。

到这里,一句话的完整配图链路才真正结束:

一句 narration
    ↓
一个 image_prompt
    ↓
一张 AI 图片
    ↓
一张模板合成画面
    ↓
一个视频片段

多个 frame 都完成后,后面的 post_production() 再把所有 video_segment_path 拼成完整视频。

十六、把完整配图流程画成一张图

现在可以把 Pixelle-Video 的 AI 配图流程完整串起来:

【文本阶段】
narrations
    ↓
generate_image_prompts()
    ↓
LLM 输出 image_prompts
    ↓
数量校验 + 失败重试
    ↓
prompt_prefix 统一风格
    ↓
ctx.image_prompts

【分镜阶段】
zip(ctx.narrations, ctx.image_prompts)
    ↓
StoryboardFrame(
    narration=...,
    image_prompt=...
)

【逐帧阶段】
FrameProcessor
    ↓
生成 TTS 音频
    ↓
根据 image_prompt 调用 MediaService
    ↓
ComfyUI / RunningHub / API 模型生成图片
    ↓
下载图片到本地
    ↓
frame.image_path = local_path

【合成阶段】
HTML 模板
    ↓
图片 + 标题 + 字幕
    ↓
composed_image_path
    ↓
图片 + 音频
    ↓
video_segment_path

这就是“每句话如何匹配一张画面”的完整答案。

十七、为什么 Pixelle-Video 要分成 image_prompt 和 image_path?

这是源码设计里很重要的一点。

image_prompt 是计划。
image_path 是结果。

StoryboardFrame 中,两者是不同字段。源码里 image_prompt 用来保存图片生成提示词,image_path 用来保存生成后的原始图片路径。

这种设计有几个好处。

第一,方便调试。

如果图片效果不好,可以先看 image_prompt 写得是否合理。

第二,方便重试。

如果生成图片失败,可以保留 image_prompt,只重新生成媒体。

第三,方便替换。

用户可以不用 AI 生成图片,而是手动设置 image_path。

第四,方便历史记录。

后续保存 storyboard 时,可以知道这张图是根据什么 prompt 生成的。

第五,方便二次开发。

未来可以增加“只重新生成第 3 帧图片”“修改 prompt 后重试”“保留图片但换字幕”等功能。

十八、为什么配图效果有时不好?

理解源码后,就能知道配图效果不好的原因通常在哪一层。

1. narration 太抽象

例如:

成长是一场内在秩序的重建。

这种句子很难生成具体画面。

即使 LLM 生成了 image_prompt,也可能会变成很抽象的符号画面。

2. image_prompt 不够具体

如果 prompt 只是:

A person thinking about life

图像模型就会生成泛泛的人物图。

更好的 prompt 应该包含:

人物
场景
动作
情绪
光线
构图
象征物
风格

3. prompt_prefix 和内容冲突

比如 prefix 是“黑白火柴人简笔画”,但 base prompt 是“超写实电影感摄影”。
两者冲突时,模型可能效果不稳定。

4. workflow 本身不适合

不同 ComfyUI workflow 质量差异很大。
同一个 prompt,换 workflow 可能结果完全不同。

5. 画面缺少连续性

每帧独立生成,可能导致角色、场景、服装不一致。
这不是简单生图错误,而是当前 frame 独立生成模式的天然局限。

十九、二次开发可以怎么优化 AI 配图?

如果基于 Pixelle-Video 做自己的短视频工具,AI 配图这一层有很多可优化空间。

1. 增加配图预览表

在真正生成图片之前,先展示:

第几帧
旁白
image_prompt
最终 prompt
模板类型
预计生成图片尺寸

用户确认后再生成图片,可以减少浪费。

2. 支持单帧重生成

当前每帧都有独立的 image_prompt 和 image_path。
可以基于这个结构实现:

重新生成第 2 帧图片
只修改第 4 帧 prompt
保留其他帧不变

这对实际做视频很有用。

3. 增加角色一致性字段

可以在 Storyboard 层增加:

main_character
location
visual_style

然后自动注入每个 prompt。

这样能减少每帧人物不一致的问题。

4. 增加 negative_prompt 配置

MediaService 已经支持 negative_prompt 参数。
可以在 WebUI 中暴露出来,让用户控制不要出现的内容,例如:

blurry
extra fingers
distorted face
low quality
text artifacts

5. 增加 prompt 质量检查

生成 image_prompt 后,可以让 LLM 或规则检查:

是否包含具体场景
是否包含主体动作
是否包含情绪
是否和旁白对应
是否太抽象
是否太短

不合格就自动重写。

6. image 模板和 video 模板分开生成 prompt

当前标准流程中,即使模板是 video,也主要调用 generate_image_prompts() 生成视觉描述。
如果要更好支持视频生成,可以让 video 模板走 generate_video_prompts(),加入动作、镜头运动、时长变化等信息。

二十、源码阅读建议

如果你要读 Pixelle-Video 的 AI 配图相关源码,建议按这个顺序:

1. pixelle_video/pipelines/standard.py
   看 plan_visuals() 如何判断模板类型、生成 image_prompts、应用 prompt_prefix

2. pixelle_video/prompts/image_generation.py
   看图片 prompt 如何要求英文、具体画面、一一对应

3. pixelle_video/utils/content_generators.py
   看 generate_image_prompts() 如何批量调用 LLM、解析 JSON、校验数量、失败重试

4. pixelle_video/models/storyboard.py
   看 StoryboardFrame 如何保存 narration、image_prompt、image_path

5. pixelle_video/services/frame_processor.py
   看 _step_generate_media() 如何拿 frame.image_prompt 生成图片

6. pixelle_video/services/media.py
   看 MediaService 如何调用 ComfyUI / RunningHub / API workflow

7. pixelle_video/services/api_media.py
   看 api/... workflow 如何走直连图像模型

这条线可以完整串起:

一句旁白
    ↓
一个视觉 prompt
    ↓
一个 frame
    ↓
一张图片
    ↓
一个视频片段

二十一、总结

这一篇我们分析了 Pixelle-Video 的 AI 配图生成流程。

它的核心链路是:

narration
    ↓
generate_image_prompts()
    ↓
image_prompt
    ↓
prompt_prefix
    ↓
StoryboardFrame
    ↓
FrameProcessor
    ↓
MediaService
    ↓
ComfyUI / RunningHub / API 模型
    ↓
image_path
    ↓
HTML 模板合成画面
    ↓
video_segment_path

从设计上看,Pixelle-Video 的配图生成有几个关键点:

不是直接拿旁白生图,而是先生成英文 image_prompt
不是随机配图,而是要求每段 narration 一一对应
不是所有模板都生图,static 模板会跳过媒体生成
不是写死某个模型,而是通过 workflow 或 api/... 路线接入不同生成能力
不是只生成图片,而是把图片继续送入模板渲染和视频片段合成

一句话总结:

Pixelle-Video 的 AI 配图,本质是把每段旁白转换成一个可执行的视觉 prompt,再通过 MediaService 生成图片,并把图片写回 StoryboardFrame,最终进入模板渲染和视频合成流程。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

天天进步2015

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值