安全场景卡视频生成
目标
输入一份岗位安全场景卡(PDF 或 PNG/JPG 图片,如《油炸岗位·电磁炉油炸机操作安全场景卡》),自动产出一条竖版学习视频:
- 1080×1920、30fps、含中文口播配音(edge-tts zh-CN-XiaoxiaoNeural, rate=+8%)
- 幕结构:封面 → 岗位风险 → 操作步骤×N → 安全红线 → 异常处置×M → 安全口号
- 时长由各幕配音时长动态决定(每幕 = voiceDuration + 0.4s 头缓冲 + 0.7s 尾缓冲)
- 画面素材优先级:卡内现场照片/裁剪区域 > ImageGen AI 插画补足
运行环境(宿主项目)
复用 Remotion 项目 /Users/00000852/WorkBuddy/2026-08-24-19-24-51/remotion-test/:
- 组件:
src/SafetyVideo.tsx(幕版式)+src/safety.ts(类型/帧数工具) - 注册:
src/index.tsx中Composition id="SafetyVideo"(calculateMetadata 动态帧数) - Python:
/Users/00000852/.workbuddy/binaries/python/envs/default/bin/python3(需 pymupdf) - edge-tts:
/Users/00000852/.workbuddy/binaries/python/envs/default/bin/edge-tts - ffmpeg/ffprobe:
node_modules/@remotion/compositor-darwin-arm64/(需export DYLD_LIBRARY_PATH)
流程(五步)
第 1 步:归档输入并解析(按输入类型二选一)
输入类型 A:PDF 场景卡
PY=/Users/00000852/.workbuddy/binaries/python/envs/default/bin/python3
ROOT=/Users/00000852/WorkBuddy/2026-08-24-19-24-51/remotion-test
cp "<场景卡.pdf>" $ROOT/inputs/<卡号>.pdf
cd $ROOT
$PY scripts/pdf2schema.py inputs/<卡号>.pdf <卡号> $ROOT
产出:
- outputs/safety/<卡号>.schema.json —— 结构化数据(meta/risks/procedures/redlines/emergencies/slogan/images)
- outputs/safety/<卡号>.review.md —— 人工复核表
- public/cards/<卡号>/xref*.png —— 提取的全部图片(含角色标注)
解析要点(pdf2schema.py 内置,排障时看):
- CJK 部首变体:PDF 文本「风飞麦车文长门」常为 U+2EDx 部首码位,NFKC 不转换,脚本用 RADICAL_MAP 显式映射。新卡出现别的「怪字」时先查码点(python: hex(ord(ch)))再补映射。
- 图片角色分类:面积 >35% 页面 或(字节 <15KB 且像素 >300×200)→ 装饰背景;像素 <120 → 图标;其余按 bbox 中心 y 分带(步骤/红线/异常)、中心 x 分左右栏,与文字块最近匹配。新卡版式不同时调阈值。
- 过滤非正文字本:meta_keywords 黑名单(编号/岗位/B级文件/岗位风险/安全场景卡/岗位名)防止元数据行混入步骤;正文上界 page_h*0.78 排除落款。
- 双栏红线/异常:按 x 列分组再合并,防止跨列串行;is_meaningful_line() 过滤纯标点行(如「||」)。
第 2 步:人工复核(必做)
输入类型 B:图片场景卡(PNG/JPG/WebP/BMP/TIFF)
支持单张或多张(多张按顺序垂直拼接,兼容手机分段拍照)。第 1 步换用 img2schema.py 与专用 OCR 环境 paddle-ocr venv,后续 2~5 步与 PDF 路径完全相同:
PYOCR=/Users/00000852/.workbuddy/binaries/python/envs/paddle-ocr/bin/python3
ROOT=/Users/00000852/WorkBuddy/2026-08-24-19-24-51/remotion-test
cd $ROOT
cp "<场景卡图1>.png" "<场景卡图2>.png" ... $ROOT/inputs/
$PYOCR scripts/img2schema.py inputs/<图1>.png [inputs/<图2>.png ...] <卡号> $ROOT
# 版面基准不符时手动覆盖:--page-w-pt 1920
产出 schema 与 PDF 路径同构,另有:
- public/cards/<卡号>/region-<field>-<n>.png —— 各步骤/红线/异常的区域裁剪放大图(photoConfidence=cropped)
- public/cards/<卡号>/full-<field>-<n>.png —— 低清降级的整卡+红框高亮图(photoConfidence=degraded,裁剪 <400×300 时触发)
- review.md 追加「OCR 与画面素材说明」:OCR 置信度表(<0.9 建议人工复核该行)
图片路径解析要点:
- OCR:PaddleOCR(score<0.6 过滤);引擎封装在 run_ocr() 可替换函数中,必要时可换 macOS Vision 兜底。
- 行→块合并:垂直间隙 <0.8×行高 且水平重叠 >0.3 合并为块,还原 PDF block 粒度(首行=标题,其余=要点)。
- 版面基准:按拼接图宽高比自动推断页宽 pt(ratio>1.6→1920 宽幅卡 / >1.0→842 A4横 / ≤1.0→595 A4竖),使 cardparse 绝对阈值语义与 PDF 路径一致;不符时 --page-w-pt 手动覆盖。
- EXIF 旋转:手机拍照自动转正;多张宽度差 >15% 时白底居中补边。
- 相机水印过滤:现场照片上的 EXIF 水印(如「24mmf/1.81/50sISO80」「2026年7月23日15:24」)自动过滤(is_watermark:水印特征正则 + 行高 <26px 启发式)。
- 拼接重叠去重:同文本 y 中心差 <15% 图高的行自动去重(多张拍照重叠区)。
- 栏目标题错字纠错:「已党处理」等 OCR 错字自动纠正为「异常处理」(同长度差异 ≤2 位),保证 cardparse 板块锚点定位。
- 裁剪区域含照片:条目区域 = 文本列 x 范围(扩展到相邻条目间隙中点)× 整个板块带 y 区间,确保区域裁剪图包含现场照片、分辨率充足。
- 口号切分:OCR 连成一行的多句口号,长度为 3 的倍数时自动三等分。
- 实测基准(C-M422-001 油炸卡,300DPI 两段拼接):5 步骤/3 红线/3 异常/6 风险全对、11 幕照片全部 cropped、成片 88.6s 与 PDF 路径一致;800px 低清图自动降级 degraded(整卡红框图),内容解析偏差需人工复核(低清 OCR 质量所限,建议重拍高清)。
OCR 依赖一次性安装(已装好可跳过;安装失败降级链见 FAQ):
/Users/00000852/.workbuddy/binaries/python/envs/default/bin/python3 -m venv /Users/00000852/.workbuddy/binaries/python/envs/paddle-ocr
/Users/00000852/.workbuddy/binaries/python/envs/paddle-ocr/bin/pip install paddlepaddle -i https://www.paddlepaddle.org.cn/packages/stable/cpu/
/Users/00000852/.workbuddy/binaries/python/envs/paddle-ocr/bin/pip install paddleocr pillow
# 验证:
/Users/00000852/.workbuddy/binaries/python/envs/paddle-ocr/bin/python3 -c "from paddleocr import PaddleOCR; print('OCR-OK')"
打开 outputs/safety/<卡号>.review.md 与 schema.json 对照原卡逐项核对:
- meta(卡号/岗位/文件级别/编号/公司)是否正确、怪字是否已标准化
- risks 数量、procedures 步数与每步要点、redlines/emergencies 条数
- 每条的照片路径是否映射对(看 images 账本中的 role/bbox)
发现错误直接编辑 schema.json(以人工修正为准,不重跑解析覆盖)。
第 3 步:schema → props
cd $ROOT
node scripts/schema2props.mjs outputs/safety/<卡号>.schema.json props-safety/<卡号>.json
# 产出 props-safety/<卡号>.json(幕序列 + 口播文案)
口播扩写规则(脚本内置,保守原则):只加连接词/称谓(如「第X步,…」「安全红线要记牢:…」),禁止编造卡片外事实;「CO₂→二氧化碳」「≥15分钟→15分钟以上」「·→,」由脚本预替换。
本步后可检查 props 中每幕 voice 文案是否顺口,必要时微调。
第 4 步:生成口播配音
cd $ROOT
node scripts/gen_safety_voice.mjs props-safety/<卡号>.json
# 产出 public/audio/safety-<卡号>-<i>.mp3 + props-safety/<卡号>-voiced.json(回填时长)
(可选分支)某幕缺照片/插画时:用 ImageGen 生成本幕主题插画,存 public/cards/<卡号>/art-<i>.png,在 props 该幕加 "art": "/cards/<卡号>/art-<i>.png"。
第 5 步:渲染与校验
cd $ROOT
npx remotion render SafetyVideo out/safety-<卡号>.mp4 --props=props-safety/<卡号>-voiced.json
校验(三项全过才算交付):
1. ffprobe:视频 1080×1920、30fps、时长=音频时长±0.5s
2. 抽帧(每 6s 一帧):文件大小 >5KB 即非黑帧;照片幕帧应明显更大
3. 播放抽查首尾幕(封面/口号)无缺字、无错位
export DYLD_LIBRARY_PATH=$ROOT/node_modules/@remotion/compositor-darwin-arm64
FM=$ROOT/node_modules/@remotion/compositor-darwin-arm64/ffmpeg
$FM -y -v error -ss <秒> -i out/safety-<卡号>.mp4 -frames:v 1 -vf scale=270:480 out/frames-safety/f<秒>.jpg
验收标准
- 输入场景卡 PDF,五步流水线产出
out/safety-<卡号>.mp4:1080×1920、含配音、时长由内容动态决定 - schema 内容与原卡逐字一致(人工复核签字确认),无部首怪字
- 现场照片正确映射到对应步骤/红线/异常幕,无背景图误入
- 无黑帧、无声画不同步(音视频时长差 <0.5s)
- 口播文案零编造:所有事实均来自卡片原文,仅连接词扩写
- (图片输入)OCR 识别内容与原卡人工核对一致(置信度 <0.9 的行重点复核);步骤/红线/异常幕使用 cropped 区域裁剪图或 degraded 高亮整卡图,无空白幕
常见问题
| 现象 | 原因 | 解决 |
|---|---|---|
| schema 某字段为空 | 文本含部首变体字符 | 查码点补 RADICAL_MAP |
| 照片映射错乱 | 版式与启发式阈值不符 | 调 pdf2schema.py 中的分带/阈值参数 |
| tsc 报 LooseComponentType 不兼容 | props 类型用了 interface | 改为 export type X = {...}(type 别名才有隐式索引签名) |
| 渲染某幕之后全黑 | Sequence 缺 durationInFrames | 每幕 Sequence 必须显式带 durationInFrames |
| ffprobe not found | 未设库路径 | export DYLD_LIBRARY_PATH=<compositor目录> |
| OCR 个别字识别错(图片输入) | 拍摄倾斜/光照不均/分辨率低 | 重新拍平正高清图;或人工直接改 schema.json(以人工修正为准) |
| paddleocr 安装失败(py3.13) | 官方包未跟上 Python 版本 | 降级链:换 paddlepaddle 3.2.0 → 改用 python3.12 建 venv → 用 macOS Vision(pyobjc)重写 run_ocr() |
| img2schema 板块识别错乱 | 版面基准与阈值不符 | 检查推断的页宽 pt 是否正确(--page-w-pt 覆盖);调 cardparse 分带阈值 |
| 图片幕画面模糊 | 裁剪区域分辨率 <400×300 | 已自动降级为整卡+红框高亮(degraded);如需更清晰请用更高分辨率原图 |
参考文件
scripts/img2schema.py—— 图片→schema 解析入口(PaddleOCR + 拼接 + 区域裁剪/降级)scripts/pdf2schema.py—— PDF→schema 解析引擎(含部首映射/图片角色分类)scripts/schema2props.mjs—— schema→props 转换与口播保守扩写scripts/gen_safety_voice.mjs—— edge-tts 配音生成与时长回填scripts/cardparse.py—— 公共解析引擎(板块识别/schema 构建/复核表,PDF 与图片路径共用)references/safety.types.ts—— schema/props TypeScript 类型定义references/example.schema.json—— C-M422-001 油炸岗位卡完整示例