代码生成视频的质检工具。渲染完一条片子,自动找出黑帧、过曝、闪烁、卡死的段落,并生成一张接触印样让你一眼扫完全片。
pip install autoqc
autoqc out.mp4 --contact-sheet现在做「代码即时间轴」的片子(Canvas / WebGL / Three.js 逐帧导出),流程都一样:
- 写
__frame(t)钩子,画面由t纯函数决定 - headless Chrome 逐帧截图、拼成 MP4
- 然后人工(或让 AI)一帧帧看有没有黑帧、闪烁
第 3 步是整套流程真正的瓶颈:渲染 4 分钟的片子要几十分钟,发现坏帧就得重跑。而且这类片子最常见的坏法非常隐蔽——某一帧里漏了一次 Date.now(),后面所有画面都错位一帧,肉眼扫过去只会觉得「好像有点怪」。
autoqc 就是把这个「扫一遍」变成一条命令:抽样、量化、定位到秒,并告诉你最可能是哪类 bug。
pip install autoqc # 只分析帧目录时不需要其他依赖- 分析视频(
.mp4/.mov)需要ffmpeg在PATH里 - 分析帧目录(逐帧导出的中间产物)只需要
numpy+Pillow - Python ≥ 3.9
装完不用 clone 仓库,直接拿你自己的片子跑:
pip install autoqc
autoqc out.mp4 --contact-sheet # out.mp4 换成你的片子
autoqc out.mp4 --preset normal # 亮调片子换这一档
autoqc shots/ # 或者直接分析导出的帧目录想立刻看到「它真的抓得住东西」,用仓库里那个故意做坏的样例(make demo 可重新生成):
git clone https://github.com/nss2018/autoqc.git && cd autoqc
make install # pip install -e .
autoqc examples/sample.mp4 --contact-sheet真实输出:
examples/sample.mp4: 16 frames sampled @ 2.0fps (preset auto)
luminance min 0.001 mean 0.100 max 0.213
visibility median 0.8470 delta mean 0.1404 max 0.4992
6 problem(s):
[ 0.00s] BLACK_SPAN 0.5 no visible content from 0.00s to 0.50s
[ 0.50s] FLICKER 4.0 4/15 sampled gaps jump far off the median (0.0158);
usually a leaked wall-clock call (Date.now / rAF)
[ 4.50s] BLOWN 1.0 highlights clipped; tame the glow or add tone mapping
[ 5.00s] BLOWN 1.0 highlights clipped; tame the glow or add tone mapping
[ 5.50s] BLOWN 1.0 highlights clipped; tame the glow or add tone mapping
[ 7.00s] BLACK_SPAN 0.5 no visible content from 7.00s to 7.50s
contact sheet -> examples/contact-sheet.jpg
report -> examples/report.json
--contact-sheet 那张图长这样(每行一帧,时间 / 亮度 / 可见度写在下面,有问题的是红框):
autoqc out.mp4 # 人类可读的结论
autoqc out.mp4 --contact-sheet # 再加一张接触印样
autoqc shots/ # 直接分析导出的帧目录
autoqc clip.mov --sample-fps 1 # 长片降低抽样率,全片覆盖
autoqc out.mp4 --preset normal # 亮调片子用 normal 档
autoqc out.mp4 --format json --out r.json
autoqc out.mp4 --strict # 有问题就 exit 1(CI 闸门)
autoqc out.mp4 --format github --strict # GitHub Actions 里直接打 annotation退出码:
| 码 | 含义 | 什么时候会 |
|---|---|---|
0 |
干净,或有问题但你没要它拦你 | 不带 --strict 时发现问题也算 0 |
1 |
发现问题 | 带 --strict 且 problems 非空 |
2 |
用错了,或者读不进输入 | 路径不存在、不是视频文件、帧目录里没图 |
退出码是被测试的(tests/test_cli.py 里同时钉住「坏片必须红」和「干净片必须绿」),
改判据时别顺手把它改坏了——一个 fail open 的门禁比没有门禁更糟。本地想看同一件事:
make demo && make gate # 坏片必须 exit 1,干净片必须 exit 0| 类型 | 含义 | 典型病因 |
|---|---|---|
BLACK |
一帧里几乎没有可见亮部 | 两个场景之间没接上,漏了一帧 |
BLACK_SPAN |
连续一段没有可见内容 | 场景切换有空档、时间轴铺不满 |
BLOWN |
高光截顶(白成一片) | glow 过曝、没做 tone mapping |
FLAT |
帧内几乎没对比度 | 填充色糊成一片 |
FLICKER |
帧间跳变远超中位数 | 用了 Date.now() / requestAnimationFrame 泄漏进来,而不是确定性帧号 |
STATIC |
有内容但一片死寂 | tween 没推进、场景卡住 |
这部分是工具里唯一真正值钱的东西,踩过的坑都写在这里。
1. 判黑不能看平均亮度。 黑底美术片(细线 + 发光字 + 纯黑背景)正常帧的平均亮度就是 0.003,一帧都很正常。用「平均亮度 < 阈值」判黑,会把整片子每一帧都判坏。真正有区分度的是可见亮部像素占比——有多少比例的像素亮过 0.15。空帧这个数接近 0,好片它在 0.001 ~ 0.05 之间浮动。
2. 不要用 ffmpeg 的 blackdetect。 它的 pix_th 默认值会把任何黑底片全片判黑。统一在抽出来的缩略图上做统计,整份报告才有一套自洽的数字。
3. 闪烁看中位数偏离,不看固定数值。 交叉淡入和硬切的底噪差一个量级;只有明显偏离中位数 4 倍 MAD(且至少 3 帧)才算闪烁,这样慢画面不会被误伤。
4. 黑场不算静止。 黑场本来就是不动的,它已经是 BLACK_SPAN 了,再报一条 STATIC 是重复报警。所以静止检测只在「本帧有可见内容」的段落里跑。
5. 连续帧分组要跟上一帧比。 写成 if i != start + 1 会在连续段里每两步误切一刀,STATIC 永远凑不满它该报的帧数(这个 bug 是真的踩过)。
判据阈值取决于美术方向,硬编码一套必然误报。默认 auto:看全片可见度的中位数,低于 2% 判定为黑底片,自动切到 dark 档。
| 预设 | 适用 | content_min |
lum_max |
|---|---|---|---|
auto |
默认,自动选 | — | — |
dark |
黑底 / 细线 / 发光字 | 0.0008 | 0.008 |
normal |
亮调、主体占满画面 | 0.010 | 0.040 |
loose |
快速迭代时只抓真洞 | 0.0002 | 0.002 |
单个阈值都能覆盖:
autoqc out.mp4 --preset dark --content-min 0.0005 --lum-max 0.006 --std-min 0.008「代码即时间轴」的片子渲染一次几十分钟,所以质检应该跟渲染串在同一条命令里,而不是等下载完再手动跑。
最简单的一体化:code-to-film 的 render.mjs 自带 --qc,渲完直接把成品交给 autoqc。
node render.mjs --page promo.html --out out.mp4 --qc # 渲完报问题,仍然 exit 0
node render.mjs --page promo.html --out out.mp4 --qc strict # 有问题就 exit 1,能当门禁
node render.mjs --page promo.html --out out.mp4 --qc strict --qc-sheet qc.jpgautoqc 不在 PATH 上时,非 strict 会跳过、strict 会明确报错退出(不会静默放过)。
二进制在别处时用 AUTOQC=/path/to/autoqc 指定。
在 GitHub Actions 里:完整可复制的流水线样例在 examples/render-gate.yml, 覆盖 Chrome + Node + ffmpeg 的装法和 annotation 输出。核心是这两步:
- run: node render.mjs --page promo.html --out out.mp4 --workers 4
- run: autoqc out.mp4 --strict --format github--format github 把每个问题输出成一行 ::error:: / ::warning:: annotation,
直接标在 Actions 的 logs 和文件视图里;--strict 把「有问题」变成退出码 1,从而 fail 掉这个 job。
想让质检只报警不拦人,去掉 --strict。
from autoqc import analyze, Threshold
rep = analyze("out.mp4", sample_fps=2, preset="dark")
for p in rep["problems"]:
print(p["type"], p["t"], p["note"])
rep = analyze(frames_dir="shots/") # 也可以吃帧目录rep["rows"] 是逐帧的全部统计量(亮度 / 对比度 / 黑占比 / 过曝占比 / 可见度),想自己接告警或画曲线直接用。
--format sarif,给 Code Scanning 用- 字幕安全区检测(底部 10% 是否被 UI 挡住)
--audio,检测静音段和爆音- 更多预设:
white(大白天片)、ui(带界面 elements 的录屏)
有想法直接开 issue 或 PR,core.py 的判据部分都是纯函数,加一个检查项大概二十行。
MIT。
