@dsh-gis/dsh-video-studio-skill 1.0.2 → 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +49 -0
- package/README.md +84 -108
- package/THIRD_PARTY_NOTICES.md +2 -2
- package/package.json +1 -1
- package/schemas/design.schema.json +5 -5
- package/schemas/film.schema.json +31 -31
- package/skills/video-studio/SKILL.md +168 -268
- package/skills/video-studio/assets/film.json +2 -3
- package/skills/video-studio/assets/golden_frames.json +11 -11
- package/skills/video-studio/assets/scenes.py +92 -49
- package/skills/video-studio/references/{style-packs.md → appearance.md} +17 -15
- package/skills/video-studio/references/audio-variation.md +73 -96
- package/skills/video-studio/references/audio.md +40 -44
- package/skills/video-studio/references/change-impact.md +2 -2
- package/skills/video-studio/references/{v08-quality.md → delivery-gates.md} +9 -11
- package/skills/video-studio/references/frame-qa.md +47 -15
- package/skills/video-studio/references/pipeline.md +4 -4
- package/skills/video-studio/references/pitfalls.md +22 -3
- package/skills/video-studio/references/proposal.md +19 -36
- package/skills/video-studio/references/quality-gates.md +11 -11
- package/skills/video-studio/references/scene-authoring.md +2 -2
- package/skills/video-studio/references/scene-grammar.md +17 -24
- package/skills/video-studio/references/sfx-design.md +7 -5
- package/skills/video-studio/references/web-engine.md +7 -7
- package/skills/video-studio/references/word-timeline.md +7 -7
- package/skills/video-studio/requirements.txt +9 -13
- package/skills/video-studio/scripts/designkit.py +84 -89
- package/skills/video-studio/scripts/filmcheck.py +19 -19
- package/skills/video-studio/scripts/filmkit.py +2949 -2948
- package/skills/video-studio/scripts/gates.py +4 -4
- package/skills/video-studio/scripts/grammar.py +10 -10
- package/skills/video-studio/scripts/musicgen.py +3 -4
- package/skills/video-studio/scripts/preflight.py +5 -5
- package/skills/video-studio/scripts/previewkit.py +11 -12
- package/skills/video-studio/scripts/propose.py +11 -11
- package/skills/video-studio/scripts/qa_frames.py +34 -35
- package/skills/video-studio/scripts/qaasr.py +26 -7
- package/skills/video-studio/scripts/qavmaf.py +19 -19
- package/skills/video-studio/scripts/render.py +137 -141
- package/skills/video-studio/scripts/scorekit.py +21 -23
- package/skills/video-studio/scripts/selfcheck.py +208 -135
- package/skills/video-studio/scripts/sfxkit.py +27 -26
- package/skills/video-studio/scripts/stagewatch.py +4 -4
- package/skills/video-studio/scripts/stylekit.py +77 -282
- package/skills/video-studio/scripts/voicekit.py +43 -44
- package/skills/video-studio/scripts/webengine.py +62 -64
- package/tools/audio_similarity.py +12 -12
- package/tools/ci_check.py +16 -16
- package/tools/estimate.py +24 -25
- package/tools/golden_frames.py +17 -18
- package/tools/silhouette_qa.py +13 -14
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
|
-
"""qaasr.py — ASR
|
|
2
|
+
"""qaasr.py — ASR 回读校验。
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
容器 / 响度 / 帧 / 音画同步这几层质检,都建立在一个假设上:
|
|
5
5
|
**成片音轨里念的就是剧本那几句话**。可是 TTS 缓存串了、换声线时漏了某句、
|
|
6
6
|
混音把某一段压没了、重新渲了但音轨是旧的——这些从容器和波形上都看不出来。
|
|
7
7
|
本模块把成片音轨转回文字,和剧本逐句对账:
|
|
@@ -66,7 +66,7 @@ tiny 只适合快速冒烟(错字多、时间戳只到整秒)。默认 model
|
|
|
66
66
|
"""
|
|
67
67
|
from __future__ import annotations
|
|
68
68
|
|
|
69
|
-
__version__ = "0.
|
|
69
|
+
__version__ = "1.0.4"
|
|
70
70
|
|
|
71
71
|
import argparse
|
|
72
72
|
import json
|
|
@@ -101,7 +101,7 @@ AUDIO_SR = 16000
|
|
|
101
101
|
OFFSET_WARN_S = 0.35
|
|
102
102
|
# 召回率的 pass / fail 线。**权威值在 render.py**(QA_ASR_RECALL_PASS / QA_ASR_RECALL_WARN,
|
|
103
103
|
# G12 门禁从这里读),本模块只是把默认值对齐,避免"同一部片、CLI 判 fail、门禁判 pass":
|
|
104
|
-
#
|
|
104
|
+
# 同一事实只能有一个权威值。
|
|
105
105
|
# 为什么是 0.85 / 0.72 而不是 0.95 / 0.90:中文旁白实测(模板片,faster-whisper small)
|
|
106
106
|
# 干净成片召回只有 0.81–0.83 —— 中英混排专名("DeepSeek 4.1 Flash"→DIXX 变体)是 ASR 的
|
|
107
107
|
# 正常误差,不是片子坏了;低于 0.72 才说明"整句丢了 / 音轨跟剧本不是一套"。
|
|
@@ -821,9 +821,13 @@ def _judge(scenes, segments, *, recall_pass=QA_RECALL_PASS, recall_warn=QA_RECAL
|
|
|
821
821
|
assigned = _assign_text(scenes, segments)
|
|
822
822
|
per_line, missing, offsets, offset_ref = [], [], {}, {}
|
|
823
823
|
tot_ref, tot_hit = 0, 0
|
|
824
|
+
lost_counter = Counter() # 全片"ref 有、ASR 没对上"的字符
|
|
824
825
|
for sc, got in zip(scenes, assigned):
|
|
825
826
|
ref = _norm_text(sc.get("text"))
|
|
826
827
|
hyp = _norm_text(got["text"])
|
|
828
|
+
lost = Counter(ref) - Counter(hyp) if ref else Counter()
|
|
829
|
+
if lost:
|
|
830
|
+
lost_counter.update(lost.elements())
|
|
827
831
|
hit = sum((Counter(ref) & Counter(hyp)).values()) if ref else 0
|
|
828
832
|
r = (hit / float(len(ref))) if ref else 1.0
|
|
829
833
|
tot_ref += len(ref)
|
|
@@ -833,6 +837,8 @@ def _judge(scenes, segments, *, recall_pass=QA_RECALL_PASS, recall_warn=QA_RECAL
|
|
|
833
837
|
"end": round(float(sc["end"]), 3), "ref_chars": len(ref),
|
|
834
838
|
"asr_chars": len(hyp), "hit_chars": hit, "recall": round(r, 4),
|
|
835
839
|
"asr_text": got["text"][:200],
|
|
840
|
+
# 单段掉分定位:这一段里"剧本有、ASR 没对上"的字(最多 6 个)
|
|
841
|
+
"lost": ["%s×%d" % (ch, n) for ch, n in lost.most_common(6)] if lost else [],
|
|
836
842
|
})
|
|
837
843
|
if ref and r < MISS_LINE_RECALL:
|
|
838
844
|
missing.append(sc["id"])
|
|
@@ -866,8 +872,21 @@ def _judge(scenes, segments, *, recall_pass=QA_RECALL_PASS, recall_warn=QA_RECAL
|
|
|
866
872
|
% (recall, recall_warn, tot_ref, tot_hit))
|
|
867
873
|
status = "fail"
|
|
868
874
|
elif recall < recall_pass - 1e-12:
|
|
875
|
+
# 只报"召回 0.813"没法修:实测掉分几乎全在中英混排专名("DeepSeek 4.1 Flash"
|
|
876
|
+
# 被听成 DIXX 之类)。这里把"剧本里有、成片里没对上"的字符按次数排出来,
|
|
877
|
+
# 让 agent 知道该改哪几个词(改读法 / 加同音别名 / 换掉 ASR 不认的写法)。
|
|
869
878
|
warnings.append("ASR 回读字符召回 %.3f 低于 pass 线 %.2f(但高于 fail 线 %.2f):"
|
|
870
879
|
"pass 但记 warning,别当完全没问题" % (recall, recall_pass, recall_warn))
|
|
880
|
+
if lost_counter:
|
|
881
|
+
tips = "、".join("%s×%d" % (ch, n) for ch, n in lost_counter.most_common(12))
|
|
882
|
+
warnings.append("成片里没对上的字(ref 有 / ASR 无,按次数):%s —— 多是专名读法,"
|
|
883
|
+
"改剧本写法或在 film.json 的 qa.asr 放宽" % tips)
|
|
884
|
+
worst = sorted((p for p in per_line if p.get("lost")),
|
|
885
|
+
key=lambda p: float(p.get("recall") or 0.0))[:2]
|
|
886
|
+
for p in worst:
|
|
887
|
+
warnings.append("最差段落 %s(召回 %.2f,%d/%d 字):没对上 %s;ASR 实际读到 %r"
|
|
888
|
+
% (p["id"], float(p["recall"]), p["hit_chars"], p["ref_chars"],
|
|
889
|
+
"、".join(p["lost"]), p["asr_text"][:50]))
|
|
871
890
|
status = "pass"
|
|
872
891
|
else:
|
|
873
892
|
status = "pass"
|
|
@@ -879,6 +898,7 @@ def _judge(scenes, segments, *, recall_pass=QA_RECALL_PASS, recall_warn=QA_RECAL
|
|
|
879
898
|
detail += ";最大偏移 %s %+.2fs" % (worst[0], float(worst[1]))
|
|
880
899
|
return {
|
|
881
900
|
"recall": round(recall, 4), "ref_chars": tot_ref, "hit_chars": tot_hit,
|
|
901
|
+
"lost_top": [{"ch": ch, "n": n} for ch, n in lost_counter.most_common(20)],
|
|
882
902
|
"per_line": per_line, "missing_lines": missing,
|
|
883
903
|
"offsets": offsets, "offset_ref": offset_ref, "offset_warn_s": float(offset_warn),
|
|
884
904
|
"gate": {"id": "asr", "title": "ASR 回读(原稿 vs 成片旁白)",
|
|
@@ -1145,7 +1165,7 @@ def report(film_mp4, tl, script=None, *, model_size="small", lang="zh", out_json
|
|
|
1145
1165
|
rep["audio_s"] = audio_info.get("duration") # 前面探测过,不重复跑 ffprobe
|
|
1146
1166
|
judged = _judge(scenes, rep["asr_segments"], recall_pass=recall_pass,
|
|
1147
1167
|
recall_warn=recall_warn, offset_warn=offset_warn)
|
|
1148
|
-
for key in ("recall", "ref_chars", "hit_chars", "per_line", "missing_lines",
|
|
1168
|
+
for key in ("recall", "ref_chars", "hit_chars", "lost_top", "per_line", "missing_lines",
|
|
1149
1169
|
"offsets", "offset_ref", "gate", "warnings", "errors"):
|
|
1150
1170
|
rep[key] = judged[key]
|
|
1151
1171
|
if asr.get("parse_skipped"):
|
|
@@ -1491,8 +1511,7 @@ def _utf8_stdout() -> None:
|
|
|
1491
1511
|
def main(argv=None) -> int:
|
|
1492
1512
|
ap = argparse.ArgumentParser(
|
|
1493
1513
|
prog="qaasr.py",
|
|
1494
|
-
description="ASR 回读校验:把成片音轨转回文字,与 timeline/剧本做字符级召回与偏移检查"
|
|
1495
|
-
"(v0.8.0)")
|
|
1514
|
+
description="ASR 回读校验:把成片音轨转回文字,与 timeline/剧本做字符级召回与偏移检查")
|
|
1496
1515
|
ap.add_argument("film", nargs="?", help="成片路径(有音轨即可,视频轨不参与)")
|
|
1497
1516
|
ap.add_argument("--timeline", default=None,
|
|
1498
1517
|
help="work/timeline.json(scenes[].id/start/end,可选 voice_start/voice_end/text)")
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
|
-
"""qavmaf.py — 全片 VMAF/SSIM
|
|
2
|
+
"""qavmaf.py — 全片 VMAF/SSIM 画质门禁。
|
|
3
3
|
|
|
4
4
|
为什么要有它
|
|
5
5
|
------------
|
|
@@ -83,7 +83,7 @@ report(film_mp4, tl, scenes_mod, script, *, mode="full", out_json=None,
|
|
|
83
83
|
· 宽高比不是 16:9 横屏(本项目只做 16:9 缩放输出);
|
|
84
84
|
· 成片尺寸与 timeline.video 声明的交付尺寸不一致(顶层规格被改动);
|
|
85
85
|
· 读不出成片分辨率。
|
|
86
|
-
-
|
|
86
|
+
- "下采样交付"是合法配置:内部构图恒 1920x1080,编码时 lanczos
|
|
87
87
|
缩到 1280x720 / 960x540。此时本模块**在成片分辨率下比较**——参考流在
|
|
88
88
|
ffmpeg 滤镜图里用 scale=w:h:flags=lanczos 缩到成片分辨率(与 FrameSink
|
|
89
89
|
编码时同口径,不用 PIL resize);返回 dict 的 ref_scale 记录映射关系。
|
|
@@ -108,7 +108,7 @@ CLI
|
|
|
108
108
|
"""
|
|
109
109
|
from __future__ import annotations
|
|
110
110
|
|
|
111
|
-
__version__ = "0.
|
|
111
|
+
__version__ = "1.0.4"
|
|
112
112
|
|
|
113
113
|
import argparse
|
|
114
114
|
import importlib.util
|
|
@@ -451,7 +451,7 @@ def _as_script(script) -> dict:
|
|
|
451
451
|
|
|
452
452
|
# ---------------------------------------------------------------- 参考帧管线(复刻 render.py)
|
|
453
453
|
def _looks_legacy(script) -> bool:
|
|
454
|
-
"""有没有"风格"信息:没有就是
|
|
454
|
+
"""有没有"风格"信息:没有就是 legacy 外观(与 stylekit 的判据一致)。"""
|
|
455
455
|
vis = script.get("visual") if isinstance(script.get("visual"), dict) else {}
|
|
456
456
|
mus = script.get("music") if isinstance(script.get("music"), dict) else {}
|
|
457
457
|
return ("style" not in script) and not vis.get("theme") and not mus.get("style")
|
|
@@ -460,9 +460,9 @@ def _looks_legacy(script) -> bool:
|
|
|
460
460
|
def _style_plan(script) -> dict:
|
|
461
461
|
"""解析剧本风格计划:**design 优先**,其次 script["_style"],最后 stylekit。
|
|
462
462
|
|
|
463
|
-
为什么必须先看 design:
|
|
463
|
+
为什么必须先看 design:"这一版长什么样"由 work/design.json 决定,
|
|
464
464
|
而 qavmaf 是**独立进程**——它拿到的 script 里没有 render 塞进去的 _style。
|
|
465
|
-
|
|
465
|
+
直接调 stylekit 会把 design 路径的片子解析成 tech-deep(默认风格),
|
|
466
466
|
参考帧用一套主题、成片用另一套:实测 VMAF 1.50、同源抽查 MAE=0.0384。
|
|
467
467
|
这不是"画质劣化",是**参考帧根本不是同一部片**。
|
|
468
468
|
"""
|
|
@@ -515,7 +515,7 @@ ASPECT_TOL = 0.01 # 宽高比容差(与 filmkit.video_plan 一
|
|
|
515
515
|
def _declared_out_size(tl, script):
|
|
516
516
|
"""交付规格:timeline.video(render.py 写出的快照)优先,其次剧本里的 video 块。
|
|
517
517
|
|
|
518
|
-
返回 (out_w, out_h);未声明时返回 None(老 timeline
|
|
518
|
+
返回 (out_w, out_h);未声明时返回 None(老 timeline 没有 video 块时的行为)。
|
|
519
519
|
"""
|
|
520
520
|
for src in ((tl or {}).get("video"), (script or {}).get("video")):
|
|
521
521
|
if not isinstance(src, dict):
|
|
@@ -538,7 +538,7 @@ def _judge_sizes(info, tl, script, comp_w, comp_h):
|
|
|
538
538
|
"scale" 成片是 16:9 下采样交付,参考流在 ffmpeg 滤镜图里 lanczos 缩到成片尺寸
|
|
539
539
|
"fail" 结构性不符:上采样 / 非 16:9 / 读不出尺寸 / 与声明交付尺寸不一致
|
|
540
540
|
|
|
541
|
-
|
|
541
|
+
合法配置是"内部构图恒 1920x1080,编码时 lanczos 缩到 1280x720/960x540"。
|
|
542
542
|
上采样不是合法交付(只会把 1080p 包装成更高分辨率),这里明确挡住。
|
|
543
543
|
"""
|
|
544
544
|
fw, fh = info.get("width"), info.get("height")
|
|
@@ -605,7 +605,7 @@ def _prepare_script(script, tl):
|
|
|
605
605
|
|
|
606
606
|
|
|
607
607
|
def _sub_kw_for(script) -> dict:
|
|
608
|
-
"""
|
|
608
|
+
"""从已解析的风格计划里取字幕样式参数(与 render.py:_sub_kw 同口径)。
|
|
609
609
|
|
|
610
610
|
⚠ 为什么参考帧必须传这些参数(实测的**误杀好片**缺陷):
|
|
611
611
|
render.py 的 frames / stills 阶段会传 design 的 visual.subtitle(size/align/
|
|
@@ -629,7 +629,7 @@ def _sub_kw_for(script) -> dict:
|
|
|
629
629
|
|
|
630
630
|
|
|
631
631
|
def _draw_subtitle_like_render(frame, text, theme, words, kw=None):
|
|
632
|
-
"""烧字幕,与 render.py
|
|
632
|
+
"""烧字幕,与 render.py 同口径(带 words 词边界折行与设计字幕样式)。
|
|
633
633
|
|
|
634
634
|
⚠ 参考帧不传 words 也是**会误杀好片**的缺陷(实测):
|
|
635
635
|
render.py 的 frames / stills 阶段调 draw_subtitle 时总会传 words,
|
|
@@ -678,7 +678,7 @@ def _draw_ref_frame(scenes, scenes_mod, script, theme, fi, fps, total):
|
|
|
678
678
|
words = fn(scenes, t, hold) if fn is not None else None
|
|
679
679
|
except Exception: # noqa: BLE001 —— 词表异常不该让参考帧渲不出来
|
|
680
680
|
words = None
|
|
681
|
-
#
|
|
681
|
+
# 字幕样式也必须与主渲染路径同源(见 _sub_kw_for 的实测说明)
|
|
682
682
|
frame = _draw_subtitle_like_render(frame, text, theme, words,
|
|
683
683
|
kw=_sub_kw_for(script))
|
|
684
684
|
fade = 1.0
|
|
@@ -705,7 +705,7 @@ def _build_filtergraph(fps, step, threads, has_ssim, vmaf_log, ssim_log, dist_fp
|
|
|
705
705
|
libvmaf/ssim 的 framesync 会按"最近的下界时间戳"凑对,容易错帧。
|
|
706
706
|
- 抽样(step>1)只对成片做 select,参考侧本来只喂抽中的帧,两边帧数天然相等。
|
|
707
707
|
- split=2:**只渲染一遍**参考帧,同时喂 VMAF 与 SSIM 两个滤镜。
|
|
708
|
-
- ref_scale
|
|
708
|
+
- ref_scale:下采样交付时,把参考流用 lanczos 缩到成片分辨率再比——
|
|
709
709
|
与 filmkit.FrameSink 编码时的 -vf scale=out_w:out_h:flags=lanczos 同口径
|
|
710
710
|
(不在 PIL 侧缩,避免两套插值口径混用)。
|
|
711
711
|
"""
|
|
@@ -923,7 +923,7 @@ def report(film_mp4, tl, scenes_mod, script, *, mode="full", out_json=None,
|
|
|
923
923
|
warnings.append("ffmpeg 构建不含 ssim 滤镜,本次只算 VMAF(SSIM 记为 skipped)")
|
|
924
924
|
rep["ffmpeg"] = ff
|
|
925
925
|
|
|
926
|
-
# ---- 3. 成片探测 +
|
|
926
|
+
# ---- 3. 成片探测 + 结构性校验(含下采样交付) ----
|
|
927
927
|
info = _probe_film(film)
|
|
928
928
|
rep["film_info"] = info
|
|
929
929
|
W, H = int(getattr(_K, "W", 1920)), int(getattr(_K, "H", 1080))
|
|
@@ -1284,7 +1284,7 @@ def draw_frame(scene, t_local, dur, t_global, script, frame):
|
|
|
1284
1284
|
fp.write(scenes_src)
|
|
1285
1285
|
script = {"title": "qavmaf selftest", "lines": [
|
|
1286
1286
|
{"id": "l01", "text": "一", "sub": "自检"}]}
|
|
1287
|
-
#
|
|
1287
|
+
# 没有内置风格表:合成剧本也必须自带一份**现场生成**的设计,
|
|
1288
1288
|
# 否则 _style_plan() 解析不出主题("不写 style 就走老外观"的兼容路径已移除),
|
|
1289
1289
|
# 参考帧无法渲染。这里用 designkit 现场采样(seed 固定 = 可复现)、
|
|
1290
1290
|
# design_to_plan 与 render 同一口径,**不引入任何写死主题/模板**。
|
|
@@ -1293,15 +1293,15 @@ def draw_frame(scene, t_local, dur, t_global, script, frame):
|
|
|
1293
1293
|
_design = _DK.sample_designs({"topic": "qavmaf selftest"}, n=1, seed=1)[0]
|
|
1294
1294
|
script["_style"] = _DK.design_to_plan(_design)
|
|
1295
1295
|
except Exception as e: # noqa: BLE001 —— 缺 designkit 也要给出可读的 FAIL
|
|
1296
|
-
print("[FAIL] 自检需要 designkit.py
|
|
1296
|
+
print("[FAIL] 自检需要 designkit.py 现场生成设计(无内置风格):%s" % e)
|
|
1297
1297
|
return 1
|
|
1298
1298
|
scenes = [dict(script["lines"][0], start=0.0, end=n / float(fps),
|
|
1299
1299
|
dur=n / float(fps), voice_start=0.0, voice_end=n / float(fps))]
|
|
1300
1300
|
tl = {"total": n / float(fps), "fps": fps, "frames": n, "scenes": scenes,
|
|
1301
1301
|
"video": {"out_w": W, "out_h": H, "scale": False}}
|
|
1302
|
-
#
|
|
1302
|
+
# 下采样交付:内部构图仍 1920x1080,交付 1280x720(编码时 lanczos 缩)
|
|
1303
1303
|
tl720 = dict(tl, video={"out_w": 1280, "out_h": 720, "scale": True})
|
|
1304
|
-
# 老 timeline
|
|
1304
|
+
# 老 timeline(没有 video 块):只按"构图同尺寸"校验
|
|
1305
1305
|
tl_legacy = {k: v for k, v in tl.items() if k != "video"}
|
|
1306
1306
|
# 非法:声明 2560x1440 输出(内部构图只有 1080p,等于上采样)
|
|
1307
1307
|
tl_up = dict(tl, video={"out_w": 2560, "out_h": 1440, "scale": True})
|
|
@@ -1376,7 +1376,7 @@ def draw_frame(scene, t_local, dur, t_global, script, frame):
|
|
|
1376
1376
|
return ok and any_w, "frames_compared=%s" % rep["frames_compared"]
|
|
1377
1377
|
|
|
1378
1378
|
def c_downscale_pass():
|
|
1379
|
-
#
|
|
1379
|
+
# 合法配置:1920x1080 内部构图 -> 1280x720 交付,必须 pass;
|
|
1380
1380
|
# 且与 1080p 同规格成片的 VMAF 差 < 3 分(阈值可比)
|
|
1381
1381
|
rep = report(film_720, tl720, mod, script, mode="full", log=None)
|
|
1382
1382
|
v1080 = report(film_ok, mode="full", log=None, **base)["vmaf"]["score"]
|
|
@@ -1454,7 +1454,7 @@ def draw_frame(scene, t_local, dur, t_global, script, frame):
|
|
|
1454
1454
|
def main(argv=None) -> int:
|
|
1455
1455
|
ap = argparse.ArgumentParser(
|
|
1456
1456
|
prog="qavmaf.py",
|
|
1457
|
-
description="全片 VMAF/SSIM
|
|
1457
|
+
description="全片 VMAF/SSIM 画质门禁:参考帧由场景函数实时渲染")
|
|
1458
1458
|
ap.add_argument("--film", default=None, help="成片路径(mp4)")
|
|
1459
1459
|
ap.add_argument("--timeline", default=None, help="work/timeline.json")
|
|
1460
1460
|
ap.add_argument("--scenes", default=None, help="scenes.py(含 draw_frame)")
|