Communitygithub.com

johnnyfekete/video-framing

专业级谈话人镜头裁切工具,集成于DaVinci Resolve Studio。自动为每个机位生成整洁居中构图,去除边缘杂物,保留装饰元素,在可见跳切处添加推近效果,并为平淡场景的关键台词添加缓慢推进。

video-framing 是什么?

这是一个专为DaVinci Resolve Studio设计的AI技能,用于自动优化谈话人头肩镜头(talking-head cut)的构图。它通过resolve-bridge与DaVinci Resolve交互,能够自动检测机位、测量面部和手部位置,并根据专业规则生成裁剪、推近和推入效果。该技能支持macOS平台,需要resolve-bridge、ffmpeg、ffprobe和Swift环境。用户需手动在decisions.json中标记杂物和保留元素,技能会生成预览供用户确认,最后自动应用为新的时间线版本。它特别适合处理构图松散、跳切明显或画面杂乱的场景。

兼容平台~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/johnnyfekete/resolve-video-skills/tree/HEAD/skills/video-framing

在你喜欢的 AI 中提问

打开一个已预加载此 Agent Skill 的新对话。

文档

video-framing 是做什么的?

在 video-tighten 之后运行(它会隐藏tighten产生的跳切),在 video-color 之前运行。需要 resolve-bridge 技能安装在其旁边(它会在 ../resolve-bridge 查找,或设置 RVS_BRIDGE 环境变量),以及 ffmpeg、ffprobe 和 swift(macOS自带;Apple Vision用于测量面部和手部)。转录需要 uv(本地Whisper)或 RVS_TRANSCRIBE_CMD 中的转录命令;如果转录失败,prepare 会继续但发出警告,转录内容将不完整。

以 python3 <此技能文件夹>/scripts/framing.py <命令> --workdir <文件夹> 运行脚本。命令包括:prepare、solve、apply、pushes。

脚本负责测量和计算数学。由你来做出判断(什么是杂物、哪些机位是平淡的、哪些台词需要推入)。**切勿停下来请求批准。**用户稍后审核。如果你不确定某个判断,仍然给出最佳答案并继续构建;将其添加到 decisions.json 的 issues 列表中(参见如何决定),apply 会将其转换为检查清单问题。reason 字段仅用于报告。

工作流程

  1. 我们身在何处。 运行 python3 <resolve-bridge>/scripts/rvs.py --workdir <文件夹> status。如果尚未跟踪任何内容,请先采用时间线(参见resolve-bridge)。

  2. 准备。 不更改Resolve中的任何内容:

    python3 scripts/framing.py prepare --workdir <文件夹>
    

    如果Resolve已打开且被跟踪的时间线包含尚未记录的手动编辑,prepare 首先会通过resolve-bridge将其记录为新版本(manual,“帧化前的手动编辑”),并告知用户。如果Resolve已打开且其缩放模式会导致 apply 拒绝操作(参见它对时间线的影响),prepare 会在开始时打印警告,以便用户在进行长时间运行前更改。

    查找镜头内元素,将其分组为机位,测量面部、眼睛、身体和手部(大约每秒4个样本),并写入:

    • generated/framing/setups/<id>.jpg:每个机位一张静止图像,带有标记的10%网格(两个轴均为0.0到1.0);
    • .edit/framing/transcript.md:MM:SS.ss-MM:SS.ss <机位> <文本>,每行对应一个在镜头内听到的句子,按时间线时间排列。每个镜头内片段只包含其自身源范围内的单词(来自Whisper的单词时间),因此不同片段之间不会重复,时间为第一个和最后一个单词的时间。直接从此类行中选择推入范围;
    • .edit/framing/decisions.json:一个模板,每个机位为空(重新运行会保留其元素未改变的机位的决策),context.json 包含预算和静止图像。

    第一次 prepare 很慢: 测量大约需要每秒钟源素材1.5秒,Whisper对每个源文件运行一次。在后台运行,对于5分钟的视频预计需要数十分钟。它会为每个元素和每个源打印进度行(已刷新,因此重定向的日志会显示)。后续运行会缓存并快速;技能更新如果改变了测量方式,会重新测量一次。

    它会为每个机位打印一行(元素、缩放预算、静止图像),以及已包含手动帧化设置的元素数量:这些元素保持原样,列表在 context.json 和 REPORT.md 中。

  3. 决定。 查看每个机位的静止图像,填写 decisions.json(参见如何决定)。然后选择推入行。

  4. 求解。 一个不更改Resolve中任何内容且从不连接Resolve的试运行。它始终基于当前版本在已跟踪状态下的记录计划(通过resolve-bridge采用的时间线)工作,因此自上次记录版本以来在Resolve中进行的任何手动编辑都不在其中:prepare 和 apply 会先记录它们。

    python3 scripts/framing.py solve --workdir <文件夹>
    

    写入 generated/framing/REPORT.md、每个机位的 setups/<id>_before_after.jpg、每个标记元素的 flags/<n>.jpg,以及 .edit/framing/solution.json。无效的 decisions.json(缺少标签、框在0..1之外)会停止并给出原因和退出代码2。

  5. 自行检查预览。 打开每个前后对比图像和标记图像。每个预览显示一个帧:该机位的第一个元素,或标记元素的中间帧。手势在其他元素或其他时刻不在此预览中,因此不仅要检查机位预览,还要检查每个标记元素的预览,并阅读标记文本。如果手在画面中位置较低(伸向镜头、在底部边缘附近做手势),即使显示的单个帧看起来没问题,也要考虑设置 "bottom_hands": "gesture"。布局:左半部分是整个源素材,绿色显示输出帧,红色显示排除框,蓝色显示保留框(推入的结束帧为青色;在关于跳过的推近或放弃的推入的标记上,被拒绝的尝试为橙色);右半部分是结果,如同观众所见,带有三分线。查找规则禁止的内容:与帧边缘相切、被裁切的手(如果手在底部边缘被裁切,同时伸向镜头或在做手势,而不是放在膝盖或桌子上,则在该机位上设置 "bottom_hands": "gesture" 并重新求解)、头部太靠近顶部、边缘仍有杂物、选中的植物被裁切。如果画面看起来错误,请修正你自己的决定(移动框、更改标签、放弃推入)并重新求解。重复直到预览正确,或者剩余问题只有人才能判断。同时阅读报告的标记和放弃的推入:报告在每个标记下提供其原始详情(框坐标、元素键);检查清单问题则得到简明的文字说明。

  6. 应用。 需要打开Resolve并加载项目。

    python3 scripts/framing.py apply --workdir <文件夹>
    

    再次求解,然后通过resolve-bridge分叉 <base>_vNN_framing(保留调色、效果和手动编辑),设置缩放、平移和倾斜(推入剪辑获得其起始帧),存档上一个版本并保存项目。记录该步骤后,每个标记和你 issues 列表中的每个条目都会成为 EDIT_CHECKLIST.md 中的一个问题,由剪辑名称和时间码定位。标记会先合并:一个机位所有跳过的推近合并为一个问题,计数其理由;同一机位的相同标记合并为一个。告知用户新的时间线名称、问题ID和报告位置。退出代码2表示预期的失败并附带消息(没有跟踪的时间线、缩放模式未经帧化校准等)。

  7. 在检查器中为推入设置关键帧。 Resolve的脚本API无法创建关键帧,因此 apply 将每个推入剪辑保留在其起始帧,写入 .edit/framing/keyframes_todo.json,并为每个推入添加一个检查清单问题,包含精确的值。如果你有计算机使用权限,请立即执行;否则告知用户这些问题由他们处理(每个剪辑大约一分钟),并且在用户下一次手动编辑同步后,由 pushes check 进行检查。

    python3 scripts/framing.py pushes begin --workdir <文件夹>     # 分叉 <base>_vNN_pushins,列出推入
    python3 scripts/framing.py pushes goto 1 start --workdir <文件夹>
    python3 scripts/framing.py pushes finish --workdir <文件夹>    # 检查关键帧,保存,关闭问题
    

    对于每个推入,在Resolve编辑页面的检查器中(视频 > 变换)。后台应用控制无法输入到Resolve中,因此这需要全屏控制:

    1. pushes goto N start。脚本移动播放头,检查器跟随。点击剪辑可能会将播放头移动一帧,因此之后不要点击时间线。检查检查器是否命名了该剪辑。
    2. 点击缩放和位置行右侧的关键帧菱形。它们会变为红色。值已经是起始帧。
    3. pushes goto N end。双击缩放X,然后全选,输入结束缩放值并按回车(缩放Y在关联时会跟随)。对位置X和Y执行相同操作。新的关键帧会自动出现。
    4. 缓动:右键点击每个红色菱形。在结束帧处,选择缓入。在起始帧处,一旦结束帧存在,选择缓入和缓出。关键帧的菜单只提供其相邻帧允许的缓动选项,并且勾选标记并不总是更新,因此检查结果:在半程处,缩放应接近两个值的平均值,在四分之一程处应低于直线值。
    5. 切勿在已设置关键帧的剪辑上通过脚本设置属性:Resolve会删除其所有关键帧。

    pushes finish 读取关键帧(通过FCPXML导出)并比较帧和值。如果缺少或错误,它会指出并保持步骤未完成。--partial 会无论如何保存,并保持这些问题未关闭。pushes check 执行相同的检查但不保存。apply --pushes fusion 会使用Fusion变换创建推入(旧方法,完全由脚本控制)。

如何决定

decisions.json:

{
  "setups": {
    "A": {
      "items": ["..."],
      "label": "plain",
      "reason": "坐在桌旁,正面,静态。右侧边缘有带磁铁的微波炉,远处左侧有空椅子;植物保留。",
      "keep_out": [[0.00, 0.45, 0.12, 1.00], [0.86, 0.30, 1.00, 0.80]],
      "keep_in":  [[0.60, 0.20, 0.74, 0.62]]
    },
    "B": {"items": ["..."], "label": "dynamic", "reason": "坐在柜台上,高角度", "keep_out": [], "keep_in": []}
  },
  "reclassify": {"V1@1234": "broll"},
  "push_ins": [
    {"start_s": 41.2, "end_s": 47.0, "text": "写作就是思考。",
     "reason": "核心论点,第一次直白地说出"}
  ],
  "issues": [
    {"text": "右侧边缘的植物与茶几一起被裁切;不确定它是否是杂物",
     "where": "S04_IMG_1234.MOV 在 00:14.00"}
  ]
}

框为 [左, 上, 右, 下],归一化到0..1范围内,基于机位静止图像(从其网格读取)。start_s 和 end_s 是来自 transcript.md 的时间线秒数。items 由 prepare 填充;请保持原样。

  • 排除框:无人选择展示的、靠近边缘的一切。电器、磁铁、空椅子、灯具、线缆、恒温器、门框、沙发的一角。求解器会尽可能将其裁切掉,但不会剪裁到手势。
  • 保留框:演讲者选择的布景装饰。植物、花卉、花瓶。求解器会保留其完整,或明确裁切掉,不留一丝。框出重要的部分(花束、植物的叶子),而不是延伸到源边缘:触及源边缘的框永远无法在裁切内完整保留。
  • 为每个机位标记为 plain 或 dynamic。plain 是指正面、眼睛水平、静态(坐在桌旁)。dynamic 是指高或低角度、不寻常的位置(坐在柜台上)或强烈的对角线。动态机位不获得推入。
  • 原因:每个机位一行,始终填写。它会进入报告。
  • 底部手部(可选):在机位上设置 "bottom_hands": "gesture"。默认情况下,触及源底部(从其高度的93%向下)的手被视为休息状态:画面可以停在它们上方。伸向镜头的手也会触及底部边缘,该规则会裁切它们。如果前后对比或标记预览显示手在底部边缘被裁切,同时手在伸展或做手势,请在该机位上设置此项并重新求解:这些手随后会被完全计入,画面会变得更加宽松。
  • 问题:每个你不确定的判断(杂物还是选择、两样东西保留哪个、你几乎选中的台词),同时应用你的最佳答案。text 说明你决定的内容以及需要人工检查的内容;where 是剪辑名称和时间码(来自 context.json),例如 S04_IMG_1234.MOV 在 00:14.00。两者都不能为空。当 apply 完成时,它们会成为检查清单问题。
  • 重新分类仅用于纠正分组:脚本视为镜头内但实际上是B-roll的元素(broll),或相反情况(on_camera)。键为 V<轨道>@<起始帧>(例如 V1@1234):即 .edit/framing/context.json 中 items 的键,其中也给出了每个元素的剪辑名称和时间码。与任何元素都不匹配的键将被忽略,因此请对照 context.json 检查。所有元素都被重新分类为 broll 的机位不需要标签。
  • 推入行来自脚本和 transcript.md:核心论点、论证的转折点、笑点、行动号召。而不是填充内容,也不是铺垫句。保持长度在3到8秒之间,且至少相隔30秒,放置在没有人声B-roll覆盖说话者的平淡机位上,并为每个编写原因。少而精优于弱行。求解器会放弃任何违反规则的行,并在报告中说明哪条规则(并在其标记预览中以橙色显示被拒绝的推入)。推入以时间线秒数记录,因此 solve 会检查每个推入的 text 是否在当前时间线的转录中:如果该台词在其范围内不再出现(时间线在你选择后发生了变化),则放弃该推入并创建一个问题以便重新选择。从 transcript.md 复制 text,以便检查能够找到它。如果没有缓存的转录,则推入会被保留,并且报告会说明未检查。

帧化规则

这些是脚本强制执行的规则。原因在此列出,以便你能够判断规则未涵盖的镜头。

基础帧,每个机位一个

  • 眼睛位于或刚好在上三分之一线上方,每个剪辑的头部空间小且相等。*眼睛靠近上三分之一线看起来自然且投入;上方墙面太多显得业余。剪到额头或触及头发的画面看起来像个错误。*当手在画面较低位置时,会将画面拉低,使眼睛移至上三分之一线上方;此时画面会向后倾斜,在相同缩放倍数下,在头部上方保留大约10%的画面高度(根据面部尺寸估计);在手势、杂物或接合处没有空间的情况下,仅由眼线决定。
  • 对于直接面对镜头说话,面部居中;为了排除杂物,最多偏移画面宽度的5%。直对镜头意味着对称;面部偏离中心看起来像个错误,除非画面明显为此构图。
  • 底部边缘永远不落在接合处(下巴、脖子、肘部、手腕、腰部...)

Individual skills in this repo

This repo contains 2 individual skills — each has its own dedicated page.

相关技能