video-to-evidence-layout — 录屏转证据截图 + 一面4块排版
2026-08-30 经 7 个版本迭代(v1 初排 → v2 裁剪被否 → v3 假对齐 → v6 漏内容 → v7 定稿)沉淀的终版流程,自动化脚本经原视频端到端实测,复现人工定稿结果。2026-08-30 夜用户终裁三项:①衔接默认 165px(见核心定则第 2 条),末段素材不足时末帧并入放长;②新增完整聊天长图交付物(见「长图拼接公式」);③长图缝处白线只裁 3px,绝不为修白边大面积裁固定 UI(见核心定则第 4 条与坑⑤)。2026-09-19 修正:修掉"段内无滚动(推进 0)"引发的重复选帧与长图越界崩溃(见坑⑦),脚本已改,无需用户干预。2026-09-29 增补:无滚动录屏(画面静止)不再是坑⑦那样的故障场景,改由 static_image.py 正面覆盖——先判有无滚动,无滚动则出逐帧过程长图或关键节点分镜长图(见「一条命令用法」的 static_image.py 段)。2026-10-07 加固网页长图分支(对抗性审查产出):stitch_pano.py 增加贴入质量门槛(--max-dist,拒贴翻页/过渡帧并告警)、兜底搜索限半径(--fallback-radius,防坑⑪跳周期)、画布上移时历史坐标同步平移(修 B2 潜伏缺陷)、改 argparse 命名参数;fix_long_image.py 默认自动备份 .bak;SKILL.md 补分段纵向合并命令。合成素材端到端实测通过(47 帧含 1 噪声帧:高度精确命中、噪声帧拒贴、内容 99.6% 行一致、备份生成、位置参数误用被拦截)。2026-10-08 新增前门类型判定与页面状态关键帧:classify_recording.py 先判录屏类型(静止/滚动/操作)再路由,别上来就跑主流程;操作型由 page_keyframes.py 按页面状态取关键帧,一页一张、自动删停留重复与过程帧。
与相关 skill 的分工(关键词分流,避免混淆)
本 skill 与 chat-recording-evidence(帧册)都处理"录屏→证据",按关键词分流:
| 用户说 | 用什么 |
|---|---|
| 录屏转截图 / 视频转证据 / 自动选帧 / 自动排版 / 一键固定录屏 / 录屏转图片(自动) / 完整长图 | 本 skill(全自动) |
| 录屏转长截屏 / 长截屏 / 长截图 / 视频转长图(录屏有滚动:聊天记录、长网页、长文档) | 本 skill:主脚本 → long_image.py(拼重叠区,处理固定UI与白线) |
| 录屏转长截屏 / 长截图 / 视频转长图(录屏画面静止、无滚动) | 本 skill:static_image.py(先判滚动 → 无滚动出逐帧过程长图 / 关键节点分镜长图) |
| 录屏转截屏(只要一张张图片,要放课件/PPT,不要证据册 PDF) | 本 skill:主脚本选帧 + dump_frames.py 导出 PNG |
| 帧册 / 抽帧 / 人工挑帧 / 逐帧定位 / 我自己选画面 / 手动截取 / 录屏抽帧 | chat-recording-evidence(人工抽帧网页工具) |
| 录屏转图片(语义模糊、未说自动还是手动) | 先问用户:要"自动一键排版"还是"人工逐帧挑" |
| 用户要自己逐帧定位关键画面(正式取证、提交法院) | chat-recording-evidence(帧册,人工抽帧) |
| 排版规则/页码规范/证据册编排与打页码 | case-file-review Phase 10(图片自动排版/页码/压缩拆分) |
| 录屏截图批量净化(去黑边·去头像·去任务栏;会议/直播成批截图) | 本 skill:scripts/cleanup.py(见 references/recording-screenshot-cleanup.md) |
| 单张超长聊天截图分页打印 / 聊天记录排到 A4 / 长截图分页 / 聊天截图做成证据材料(输入是现成图片,非录屏) | 本 skill:scripts/split_to_a4.py(原 chat-screenshot-to-a4 并入;按消息空隙分页,绝不切断消息,时间戳不与其下方内容分家) |
| 录屏转关键帧 / 按页面状态取帧 / 操作演示·网页浏览录屏(页面跳变+停留+重复浏览,非滚动内容) | 本 skill:classify_recording.py 判型 → page_keyframes.py(一页一张,自动删停留重复与过程帧) |
> 命名约定:本 skill 输出目录/PDF 前缀为「录屏证据_」(静止画面长图分支的输出目录前缀为「录屏长图_」,文件名 录屏逐帧过程长图.png / 录屏关键节点分镜长图.png);「帧册」一词专属于 chat-recording-evidence,本 skill 的产物命名与文案中不使用该词。
第 0 步:先问目的(动手前必做,2026-10-09 立)
录屏素材动手前,先确认用户要的最终产物,一轮问清(非交互环境按默认走并打印提示;用户已说清目的时跳过):
| 问:你要的产物是? | 去向 |
|---|---|
| ① A4 证据册(法院提交、打印用) | 主流程 record2evidence.py |
| ② 完整长图(证明内容一条不漏) | 聊天录屏 long_image.py;滚动列表/网页/App stitch_pano.py;静止画面 static_image.py |
| ③ 视频卡片/轮播的单帧定格(证明"此处有个视频") | 选帧后 dump_frames.py 导出 PNG(不拼长图) |
| ④ 逐帧图组(证明视频播了什么内容) | 密集抽帧出图组;要人工挑帧转 chat-recording-evidence(帧册) |
| ⑤ 已有超长截图分页排 A4 | split_to_a4.py |
| ⑥ 聊天中保存的照片/文件排版 | photo_layout.py |
> ⚠️ 素材含自动轮播横幅/自动播放视频卡片时,必须主动告知:滚动拼接的假设是"页面是一条固定的布,滚动只是移动取景框",而轮播区"布上这块地方自己会换画"——长图是一个"时刻"的产物,最多定格一帧,另两帧必丢,且这块"变"会把旁边静止内容的定位带歪。拼长图对它无解(浏览器整页截图能"包含"轮播是因为渲染引擎冻结动画,App 录屏无此特权)——引导用户改走 ③ 单帧定格或 ④ 帧组。工具对动态区域的帧会拒贴并列入告警清单,那正是"这里有动态内容"的标记。
>
> 一句话心法:开口第一句先说截图目的——"证明这里有视频"→③;"证明播了什么"→④;"完整还原页面"→②。目的说清,才知道走哪条产线。
>
> 两个问句一轮问完:①要什么产物(本节六选一)→ ②拍的是什么(素材类型:微信滚动/网页操作/多 APP 切换,见下方「第 0 步·先问一句 + 录屏类型判定」,classify_recording.py 脚本判型交叉验证)。先目的后素材,两条答案合起来才定产线——素材含轮播/视频卡片且用户又要长图时,先劝改③/④再动手。
>
> 网页录屏的顺带引导(2026-10-09 立:提示,不拦截):判型为"滚动"且画面可见浏览器地址栏/标签栏(单网页浏览)时,顺带提示一句:"如果你只是要这张页面的长图,把网址发我用 WorkBuddy 直接截更快更清晰(引擎级渲染,文字更锐利);如果这段录屏本身是取证过程——要证明何时、如何操作到这个页面——就继续当前路线。"用户不回应或选录屏路线,照常跑,绝不因提示中断流程(快是硬要求)。判定不确定(App 内嵌页、登录后页面、页面可能已失效——录屏是唯一载体)时不提示,直接走录屏路线。
核心定则(用户拍板,不可违反)
- 绝不裁剪:每帧都是原始完整截图。裁过的图"变短了",不是原始截图(v2 被否)
- 相邻两图衔接 = 下图压上图 165px(默认,约一行多微信消息,该视频实测实际衔接 159-163px):选帧目标是「内容窗口 − 165」即推进 ≈ 窗口 − 165。⚠️ 因帧间滚动离散,名义 overlap 与实际衔接会有偏差(本视频 165 档实测 159-163px),只要衔接 >100px 即不断档。若视频总滚动量不足(末段推进 < 目标),末帧照常并入放长,允许多压前一张图信息——这是素材决定,不是缺陷(用户 2026-08-30 裁定)
- 推进量不得超过内容窗口高度:录屏界面顶部状态栏/导航栏、底部输入框是固定 UI,不随滚动移动,实际内容窗口 = 屏高 − 固定区(微信聊天页本视频实测 1128px = 1280 − 固定UI上11/下139;文章页约 1165-1196px;小屏机型窗口更小,见坑②的 992px 案例)。推进超过窗口高度 = 中间漏内容
- 长图拼接缝处白线只裁精确像素行(用户 2026-08-30 终裁 3px):白线 = 每帧段底的输入框上沿白条(约 2px,行 mean 240-242 且 std<5)+1px 过渡行(mean ~223 std~11)。修白边前必须逐行测量缝 ±10px 的 mean/std 定位白线精确行数,只裁问题行——绝不为此大面积裁标题栏/输入框/工具栏(实测教训:裁掉 45px 标题栏导致文字丢失,被用户否决"你把原来的文字给修复掉了,白边很细很细")。先按用户口径试裁(5px 嫌大 → 3px 定稿)
- 快是硬要求(用户 2026-09-23 拍板,与前面四条同级):一次「录屏转截屏 / 录屏转证据」的 agent 代跑默认 ≤2 分钟交付(转换本身实测:主脚本 44s + 长图 31s + 截屏导出数秒)。为此明令禁止三种自造开销:① 为"刷新"重跑与已有成果等价的整链(先比等价,等价就只补缺的那一件);② 通读脚本源码(本文件已给 CLI,只在报错时读);③ 逐页读图复核(脚本已打印衔接量与缝处 mean/std,agent 只对 ⚠ 行读图)。小样图逐页目检是留给人的复核动作,不是 agent 的工序;"先确认再拼长图"的顺序不变,只是 agent 的确认手段从"逐页看图"换成"读脚本定量输出 + 只查告警"。落地口径见「代跑提速口径」
一条命令用法
python3 ~/.workbuddy/skills/video-to-evidence-layout__skillhub/scripts/record2evidence.py 录屏.mp4 -o 输出目录
完整长图(独立脚本,按需单独生成,主脚本跑完后执行):
python3 ~/.workbuddy/skills/video-to-evidence-layout__skillhub/scripts/long_image.py 输出目录
⚠️ 先判有没有滚动(long_image.py 的前提是画面在滚;静止画面推进量恒为 0,拼不出来):
python3 ~/.workbuddy/skills/video-to-evidence-layout__skillhub/scripts/static_image.py <录屏> -o <输出目录>
> 这个脚本自带前门判断:先跑滚动检测(中心区裁剪 + 逐帧小步长 MAE 对齐,与 shift_of 同源)。
> 检出有滚动 → 直接退出并打印上面那两条主流程命令(有滚动的素材拼重叠区才对,且那边会处理固定UI与白线);判定无滚动 → 出长图。确要强行按无滚动处理加 --force。
> 两种出图形态:--mode frames(默认)每 --interval 秒一行、铺满全程,左侧时间轴栏;--mode storyboard 只取关键时刻(--times 0,6.5,13 指定,不给则按 0/35%/60%/85% 自动取 4 个)。默认输出目录 ~/Desktop/录屏长图_<视频名>。高度上限 120000px,超了会提示把 --interval 调大而不是产出一张打不开的图。
第 0 步·先问一句 + 录屏类型判定(2026-10-08 新增:先问"拍的是什么",再脚本判型交叉验证):
> agent 代跑先问用户一句话:「这段录屏拍的是什么——微信聊天或长文滚动、电脑网页操作、还是手机多 APP 之间切换?」用户口径是第一信号、零成本;脚本判定做交叉验证,两者矛盾时以用户口径为准并打印告警。三种素材难度递增:
> - 微信聊天或长文滚动(单页面连续滚动——聊天记录与公众号长文/网页长文档性质完全一样,内容相对稳定)→ 滚动型主流程 + long_image.py,这正是主流程原生设计的场景
> - 电脑网页操作(标签页切换、页面停留、重复浏览)→ 操作型 page_keyframes.py,一页一张
> - 手机多 APP 切换(最复杂:转场动画帧多、状态栏/刘海/底部手势条是固定 UI、横竖屏混排、App 内部还可能既有页面切换又有长列表滚动)→ 先 classify_recording.py 判型,操作型走 page_keyframes.py(转场动画帧会被稳定段逻辑自然剔除);若 App 内有长列表滚动则该段再回主流程分段处理;注意手机竖屏分辨率与坑⑥固定区检测
python3 ~/.workbuddy/skills/video-to-evidence-layout__skillhub/scripts/classify_recording.py 录屏.mov -o 输出目录
python3 ~/.workbuddy/skills/video-to-evidence-layout__skillhub/scripts/classify_recording.py 已有帧目录/_raw # 帧已抽好直接判,不重抽
> 判据全量化:①突变帧(相邻帧 MAE≥2.5)≤2 个 → 静止型(判据是突变数,不是稳定占比——操作型录屏同样 99% 时间静止但突变多,第一版按"稳定占比>95%"判静止实测误判,已修);②突变帧竖向平移对齐(±80 缩略图像素)后残差 <0.4×原始且 <8 = 可被滚动解释,滚动步进 ≥10 且 ≥突变半数 → 滚动型;③对齐不掉残差 = 内容整体替换(翻页/切标签)→ 操作型。判完即路由:静止→static_image.py;滚动→主流程+long_image.py;操作→page_keyframes.py。
操作型录屏 → 页面状态关键帧(一页一张;自动删停留重复、打字下拉、加载空白等过程帧):
python3 ~/.workbuddy/skills/video-to-evidence-layout__skillhub/scripts/page_keyframes.py 帧目录或视频 输出目录 --fps 15
> 四步全自动:①缩略图(160px灰度)逐帧 MAE 切稳定段(停留 ≥0.4s 才算,打字下拉/加载空白等过程段被丢弃)②段内取中位帧(避开段首尾过渡边缘)③允许±26px 竖移对齐的两两比对(解决"同页滚一点被误判成新页")④对齐 MAE<3 判同页归并(保留停留更长者)。⚠ 坑:对齐搜索的 range 起点与步长必须保证 s=0 被取到(range(-26,27,2) ✓;range(-25,26,2) 从 -25 起步进 2 永远取不到 0,实测导致同页归并全部失效,见坑⑯)。产出 <输出目录>/状态关键帧/(整帧 PNG + 报告 + 小样);3≤对齐MAE<6 的"疑似同页"对在报告里列出供人工定夺,绝不静默删。实测:1580 帧 → 17 稳定段 → 16 状态帧 → 自动归并 7 张重复 → 10 张(人工终选 6 张页面关键帧)。
⚠ 排版前必须剔除加载/骨架帧(用户 2026-10-08 拍板):手机 APP 录屏的状态帧里混着骨架屏(灰色占位块)、"正在加载"转圈页、纯 logo 启动页——这些对证据无价值,排 PDF 前必须剔掉,只留有实质内容的帧。流程:scripts/skeleton_check.py 状态帧目录 出量化打分表(骨架灰占比/彩色占比/边缘密度,骨架灰>10%且彩色<6% 或边缘密度<1.2 标疑似)+ _编号小样_终审.png → agent 读一张编号小样逐帧定性(单指标噪声大——APP 蓝色渐变头会推高"彩色占比",纯 logo 页靠边缘密度兜底;最终以小样目审为准)→ 保留清单显式传给 photo_layout.py 重排 + 写「内容帧终审报告.txt」(保留/剔除明细,剔除的原 PNG 不动可回溯)。启动页/纯 logo 页默认删。实测:20 状态帧 → 剔 9 张(骨架×3、加载×2、启动页×3、同页重复×1)→ 留 11 张有实质内容。出图后接排版别断链:需要 A4 证据册时 photo_layout.py 状态帧_*.png -o <输出目录> --per-page 2 --group <组号> --yes(手机竖屏截图 2 张/页一上一下单格最大;桌面横图可 --per-page 4)。⚠ 传目录会把目录里的小样拼图 PNG 一起排进去(实测 20 帧排出 21 张)——代跑一律用显式文件列表,产物 PDF 改名为「照片排版_<内容>_组<N>_一面2张.pdf」便于归卷。
滚动列表 / 网页 / App 界面录屏 → 一张完整长图(独立脚本,全自动;2026-10-05 经 11 轮返工定稿,2026-10-07 加固):
# ① 抽"原始帧"(关键:切勿用 -vf fps=NN,会插出鬼影帧,见坑⑩)
ffmpeg -y -v error -i 录屏.mov -fps_mode passthrough -q:v 1 帧目录/f_%05d.png
# ② 抽看首/中/末 3 帧 → 识别内容状态(骨架屏?翻页?页脚?),定分段与起始帧
# ③ 分段拼接(稀疏选帧;每段一个起始帧,见下方"流程四步")
python3 ~/.workbuddy/skills/video-to-evidence-layout__skillhub/scripts/stitch_pano.py <抽帧目录> <段输出.png> \
--top <固定UI行数> --start <起始帧索引> --end <结束帧索引> --min-step 500
# ④ 后处理(边缘暗列/硬缝线/错位线)+ 用户圈定区域定点清线(默认自动备份 .bak)
python3 ~/.workbuddy/skills/video-to-evidence-layout__skillhub/scripts/fix_long_image.py <长图.png> [y0-y1] [y0-y1] ...
# ⑤ 多段纵向合并(翻页录屏分段拼接后)
python3 - <<'PY'
from PIL import Image
segs = ["段1.png", "段2.png"] # 按顺序列出各段
imgs = [Image.open(p) for p in segs]
c = Image.new("RGB", (imgs[0].width, sum(i.height for i in imgs)), "white")
y = 0
for i in imgs:
c.paste(i, (0, y)); y += i.height
c.save("合并长图.png")
PY
> 与主流程的分工:主流程按内容推进选帧排 A4 证据册;本脚本把整页滚动拼成一条完整长图。
> 参数防呆(2026-10-07):--start/--end 是帧文件在排序列表中的索引(0 起),不是帧号——f_00470 = 索引 469。所有参数均为命名参数(argparse),不再是位置参数。
> 贴入质量门槛(2026-10-07):匹配距离 > --max-dist(默认 8.0)的帧拒贴并告警(翻页/过渡/半透明帧贴入会不可恢复地污染画布)——见 ⚠ 输出须人工核对拒贴区间是否漏内容,确属翻页则分段重跑。
> 兜底限半径(2026-10-07):局部失配后的兜底搜索限制在 prev_top ± --fallback-radius(默认 1000)内,防周期列表跳周期错位(坑⑪);周期列表素材务必让该值 < 卡片周期。
> 备份(2026-10-07):fix_long_image.py 原地保存前自动备份 <长图.png>.bak(已存在则不覆盖,始终保留最初原件);--out 可另存、--no-bak 跳过。
流程四步(顺序不可颠倒)
- 抽帧:必须
-fps_mode passthrough。录屏常是 VFR(本例容器标 120fps、实际 636 帧),fps=NN重采样会插出"半混合"帧 → 任何位移都匹配不上(对齐曲线整条平坦)。 - 前置检查(最容易漏、代价最大):动脚本前先抽看首/中/末 3 帧,确认内容状态——
- 骨架屏(加载占位):起始帧必须选"内容稳定帧",否则骨架灰底会污染所有"未写入行",卡片底色全错、末尾混入骨架块(本例第 2 页真内容从 f_00470 才开始,338-464 全是骨架屏);
- 翻页/内容突变:按定位残差突变点分段,翻页后与前面无重叠,硬拼必错位;
- 页脚:从"滚到最底"的干净帧取,别从拼接画布上取(会带入错误定位的残影);
- 同一 UI 是否重复出现:标题栏/搜索栏/页脚在长图里往往出现多次。用户报"某处有问题"时,先算用户截图的缩放比(截图内元素高度 ÷ 长图内同元素高度)反推坐标,别默认第一处(本例为此白修了两轮)。 - 拼接:全景锚定(特征=32 段灰度均值⊕32 段灰度标准差;只写新增行;局部搜索 ±300px)+ 稀疏选帧 MIN_STEP≈500px——逐帧小步拼接会让每个贴帧边界留 1px 错位线,微信式"每帧贴 25px"产出密密麻麻的百叶窗纹;稀疏后边界少一个数量级。循环后必须强制贴入末帧,否则列表末尾被步长截断。
- 后处理:
fix_long_image.py清三类拼接瑕疵——左右边缘暗列(窗口边框被拼进来)、硬缝线(dev<-18)、贴帧边界±3行错位线。
> ⚠ 保持原样原则(用户 2026-10-05 明确,最高优先):不做背景白化、不做底色归一、不做大面积暗带填充。页面原生的底色(灰卡/白卡)、hover 悬停灰、分隔线与虚线一律保留。后处理只允许修"拼接引入"的瑕疵——判断依据是与原始帧逐行对比:灰度一致=原生,保留;只在长图出现=瑕疵,修。
> 用户圈定区域的处理:跑 fix_long_image.py 长图.png y0-y1(孤立线判据:行差>0.9、跨行相似<5、行内近纯色、±2行内无其他大差异;迭代须递减,不递减=判据发散立即停)。切勿全图低阈值迭代(见坑⑬b)。
> 原生但用户嫌硬的线:不要删除元素本身,用上下行插值把它变成 2~4 行渐变(元素仍可辨识、线感消失)。
> top 参数=画面顶部固定 UI 行数(先测:取相隔较远两帧逐行算 |a−b|.mean,<3 的连续行即固定区)。
聊天里保存的照片/文件证据排版(独立脚本,与本 skill 主流程分工:主流程管录屏→截图,本脚本管照片→排版):
python3 ~/.workbuddy/skills/video-to-evidence-layout__skillhub/scripts/photo_layout.py 照片1.jpg 照片2.jpg ... -o 输出目录
python3 ~/.workbuddy/skills/video-to-evidence-layout__skillhub/scripts/photo_layout.py 照片目录/ -o 输出目录 --group 3
> ⚠ 先问后排(用户 2026-10-08 拍板,最高优先):agent 代跑排版前必须先问用户排版方案(AskUserQuestion 列出 1张关键凭证 / 2张一上一下 / 2张横版左右 / 4张2×2 等选项),确定方案后再跑脚本——绝不许静默用 --yes 落默认(2026-10-08 教训:20 张状态帧未经询问直接排成一面2张,用户指出 skill 本有版式选项)。--yes 仅限两种情况:用户已明确表达方案、或同方案重跑。网页截屏/屏幕截图进入排版环节同样先问。
>
> 脚本自带交互菜单:不传 --per-page 时先展示版式菜单(每页1张关键凭证/2张一上一下/2张横版左右/4张截图2×2)请用户选择,再问组号;排版规则源自 case-file-review「图片自动排版」(与帧册 Framebook 一致)并按用户习惯调整:照片类每页 2 张默认竖版一上一下(横拍照片单格比横版左右格大 33%,且全卷 A4 方向统一便于装订翻阅);--landscape 切横版左右两格(竖拍照片为主的场景更大);--per-page 1 关键凭证竖版整页;--per-page 4 截图类竖版 2×2。页码「组号-页号」16pt 底部居中(--group 定组号,--no-page-number 可关闭——用于页码待材料编排完成后统一回填的场景);图片等比缩放完整居中绝不裁剪绝不变形;手机照片自动 EXIF 方向归一化;支持 jpg/png/bmp/tiff/webp 与 PDF(逐页转图);页数自检内置。来源标注 caption(2026-10-08 用户拍板:所有录屏产物一律打标,法律溯源用):每张图下方 8.5pt 灰字标注「取自录屏 00:27.7 · 帧 f00436」(秒数固定一位小数)。两条链路全覆盖:① 主流程 record2evidence 默认打标(--no-caption 关;帧 N 时间=(N-1)/fps,标注留 10pt 格子预留不压图);② photo_layout 对帧册命名输入(状态帧_/关键帧_/截屏_ 等)自动打标并打印提示,照片类不标,--caption 强制全标、--no-caption 强制关;中文标注嵌 PingFang(回落 Hiragino/STHeiti,缺字体告警出方块)。⚠ 传目录的坑(2026-10-08):目录下所有图片(含小样拼图等非素材 PNG)都会被排进去,实测 20 帧目录排出 21 张——素材与产物混放时必须用显式文件列表(如 状态帧_*.png)。用户把聊天记录中保存的照片/文件直接传入即可出证据排版 PDF。
> 长图脚本读取主脚本的 selection.json 与 _raw/(人工复核确认前勿删 _raw/);内容窗口与衔接量从选中帧自适应重算、不写死分辨率;段底白线裁除默认 3px(--trim-bottom 可调);自动打印缝处 mean/std 验证与总高校验(Σ段高 = 总推进+内容窗口+fixed_top−段数×裁线量)。
要一张张独立截屏 PNG(课件/PPT 配图、要点稿插图用——PDF 与长图都给不了这个):
python3 ~/.workbuddy/skills/video-to-evidence-layout__skillhub/scripts/dump_frames.py 输出目录
> 截屏导出(独立脚本,主脚本跑完后按需执行):导出的是选中帧的原始整帧,不裁剪不缩放,与 PDF 里用的是同一批图,故两者内容必然一致;跨组连续编号 截屏_01_组1.png…(组号后缀便于回溯来源),同时写 截屏清单.txt(序号/组号/源帧号/尺寸)。selection.json 中的路径失效时(成果包被整体拷贝到别处,如换一场讲座的素材目录)按文件名回落本目录 _raw/ 查找。依赖 _raw/,中间帧已删则需重跑主脚本。
已有超长聊天截图(不论来源——微信/短信导出长图、网页长截图等,输入是现成图片而非录屏)分页排 A4 打印(独立脚本,不依赖主流程,原 chat-screenshot-to-a4 并入):
python3 ~/.workbuddy/skills/video-to-evidence-layout__skillhub/scripts/split_to_a4.py "输入长截图.jpg" "输出.pdf" --crop-top 145
python3 ~/.workbuddy/skills/video-to-evidence-layout__skillhub/scripts/split_to_a4.py "输入长截图.jpg" --probe # 只打印顶部界面分界,帮助确定 crop-top
> 三条硬约束:切分点只落在消息空隙内绝不切断任何消息/截图;时间戳不得与其下方内容分到两页(半透明浅色框按低饱和度+居中+窄宽识别,其下方空隙禁切);清晰优先,自动搜宽下限锁版心宽 85%。空隙检测用逐行灰度 std 低分位(p8×1.25,禁用 p20 以上)定阈值,相邻空隙 12px 内合并为逻辑空隙;自动搜宽取「页数最少」者中图宽最大一档。--crop-top 裁手机状态栏(先用 --probe 定界),微信标题栏(对话对象昵称)必须保留。输出 PDF + 同名 _preview.png 小样,小样必须人工过目确认页首页尾衔接自然。常用参数:--margin-mm 15、--dpi 150、--cuts y1,y2,…(手动切点)、--no-ts-guard(无解退路)。A4@150dpi = 1240×1754px,版心(15mm 边距)1062×1576px。
常用参数:--overlap 165(衔接像素,默认)|--per-page 4(每页1/2/4/6张)|--fps 15(抽帧密度)|--no-caption(关闭每张截图下方的「取自录屏 时间·帧号」来源标注;默认打标)|--img-scale(放大系数;6块版式默认即 1.03 定稿值,其他版式默认 1.0,可显式覆盖)|--fixed-top / --fixed-bottom(手动覆盖固定UI像素,支持按组 '2:140' 或 '1:147,2:140';白底页面自动检测失效时必用,见坑⑥;主脚本与 long_image.py 均支持)
> 每页 6 张(3列×2行,省纸版式)——定稿参数(用户 2026-08-31 拍板):肩并肩无缝隙 + 放大 3%(img_scale=1.03,单张 6.18cm),放大宽度由边距自动消化(左装订边收至 1.3cm、右边距 1.2cm),图片绝不压叠;阅读顺序左→右、上→下;每张图右下角白底黑字跨页连续顺序编号 1,2,3…;页面底部不打页码。选 6 块时无需传 --img-scale,默认即 1.03;聊天截图多、想省纸用 6;要求单张更大用默认 4。
> 默认 165 已兼顾该视频的「内容连续 + 不断档」,特殊视频可在此值 ±30 微调。末段因素材不足衔接偏大属正常(见核心定则 2)。
> 关于 --per-page 1/2:布局始终按竖版 A4 固定两栏排,因此 1 张时落在左上格、2 张时占上排且下半页留白——这是已知的既有行为,不是缺陷。手机录屏场景默认用 4 张即可;1 张可临时用作关键证据放大,2 张对竖长截图无几何优势(横版 A4 的 2 张模式属照片类证据,归 case-file-review 管)。
流程(脚本自动化 6 步 + 1 步人工)
- 侦察:ffprobe 取时长/分辨率
- 密集抽帧:15fps(滚动快的录屏加密到 20fps);随后自动检测并裁除编码黑边(见坑④),只去补边不动屏幕内容
- 逐帧滚动偏移:相邻帧小步长(上限 0.6 屏)灰度 MAE 对齐,得每帧推进量——全屏搜索必假对齐,见坑①
- 自动分段:原地内容突变(shift≈0 且 MAE>18)判场景切换;短于 0.5s 的过渡段(半透明滑入动画)自动丢弃;每段按行方差自动测内容窗口
- 选链:段内累加推进,达「内容窗口 − overlap」选帧;末帧并入(若与上帧推进 <0.15 窗口则替换,去停顿重复)
- 排版+自检:A4 竖版(每页1/2/4/6张可选)、等比完整居中不裁剪、每张图右下角跨页连续顺序编号 1,2,3…(白底黑字小标签)、阅读顺序左上→右上→左下→右下;页面底部不打页码(2026-08-31 用户拍板取消,图片编号已可定位);自检页数/帧数/绘制尺寸一致性
- ⚠️ 复核顺序铁律:先复核、后长图(必做):跑主脚本前先问用户两件事——①要一面4块(单张更大)还是一面6块(省纸,单张6.0cm,微信截图优先建议6块);②要不要顺便生成完整聊天长图。主脚本跑完后,必须先把
小样图复核/逐页看完(查重复图/断档)+ 核对选帧报告.txt衔接量,确认无误再执行 long_image.py。带着已知异常往下跑,只会让注定失败的脚本先跑一轮、白等一次(2026-09-19 实测:末尾静止段造成的重复帧在小样图上肉眼可见,却先跑长图、直到崩溃才发现)。全部确认后再删_raw中间帧目录。(agent 代跑提速口径见定则⑤:确认依据以脚本定量输出为主——衔接量 + 缝处 mean/std;只对 ⚠ 行看图,小样图逐页目检留给人。) - 完整长图拼接(用户要才执行,独立脚本):
long_image.py单独执行(用法见上),见下方「长图拼接公式」
长图拼接公式(2026-08-30 实测定稿,以 590×1280 微信录屏为例;脚本已自适应任意分辨率)
将选中帧按像素级对齐拼成一条完整聊天长图——内容一条不漏、缝处无白线,是"内容完整性"的可视化证据(长图 + PDF 双交付:长图证完整,PDF 证排版)。
段起点:
帧1 = y 0(保留状态栏+标题栏作长图开头上下文)
帧2/3/4 = y (fixed_top + 衔接量) # 跳过与上帧的重叠区,fixed_top=11
段终点:
统一 y = 1138 # = 内容窗口下沿(1139) 再向上裁 3px 白线(用户终裁)
白线定位法:逐行测段底 ±10px 的 mean/std,
白条行 = mean 240-242 且 std<5(输入框上沿 2px)
过渡行 = mean ~223 且 std~11(1px)
内容行 = std 40-60 —— 只裁前两种,内容行一个像素不动
画布背景:微信灰 #EDEDED (237,237,237)
总高校验:Σ段高 = 总推进 + 内容窗口 + fixed_top − 段数×裁线量(本视频 2449+1128+11−4×3=3576),不符=对齐错误
验证:拼接缝 ±6px 逐行 mean/std——上下行 std 都应 >40(内容行直连,无亮线)
> 缝2/缝3 若衔接量大(如末段 613px),缝两侧是同一片内容,视觉完全无缝;缝1 衔接小时(159px)缝处是灰背景对灰背景,同样无白线。
十五个致命坑(都真实踩过)
| 坑 | 症状 | 根因与对策 |
|---|---|---|
| ① 全屏搜索 shift 假对齐 | 报告称"重叠130px"实际压了半屏 | 慢滚动录屏画面大量白底,大范围 shift 搜索会饱和到上限得到假匹配(v3 假象)。只能相邻帧小步长(≤0.6屏)逐帧算,再累加 |
| ② 推进超内容窗口 | 相邻图之间整段气泡开头消失(内容断档) | 固定 UI 占掉 200-290px,可滚动窗口远小于屏高(v6 推进1150>窗口992 漏内容)。推进上限 = 内容窗口,衔接目标 = 窗口 − 165 |
| ③ 裁剪"缩短"重叠 | 图变短,证据失真 | 重复内容多时的错误解法是裁图。正确解法永远是回视频重新选帧 |
| ④ 编码黑边 | 每张截图右/下有细黑边 | 编码器把画面补齐到 8/16 倍数(如 589→592、1277→1280),补出的 2-3px 纯黑边烙在每帧边缘。脚本自动检测并统一裁除——只去编码补边、恢复真实屏幕区域,屏幕内容一个像素不动,不违反"不裁剪"定则 |
| ⑤ 修白边裁掉文字 | 用户:"你把原来的文字给修复掉了,那个白边很细很细,你只要修 3px 就行了" | 错误做法:为消长图缝处白边,大面积裁标题栏(45px)+输入框(6px),把聊天文字一起裁掉。正确做法:先逐行测量缝 ±10px 的 mean/std,白线实际只有 2px 白条+1px 过渡行,只裁这 3px。修任何"细线/细边"前必先精确定位行数,禁止按经验扩大裁剪范围 |
| ⑥ 白底页面固定区检测失效(2026-09-05) | 长图里固定栏重复出现 N 次(如公众号文章页作者栏每 ~1100px 重复一次);选帧推进超真实窗口导致断档 | 按行方差检测固定 UI 依赖"固定区各行稳定",但白底文章页(公众号文章/网页)状态栏和吸顶/底栏区域行方差虚高、正文又是纯白,自动检测出 fixed_top=16/固定下66 的错值。**检测方法:取同段两帧逐行算 |
| ⑦ 段内无滚动(推进 0)(2026-09-19) | ①同一画面被选成两张相同截图(PDF 出重复图);②长图脚本 ValueError: Coordinate 'lower' is less than 'upper' 或 IndexError: list index out of range 崩溃 |
录屏末尾静止看某一屏(点开图片不动、停在最后一屏)时整段 adv≈0,触发两处连锁失效:(a)选帧收尾 原条件 gap < 0.15*W and len(chain) > 1 中 len(chain)>1 让"末帧替换"失效——段内 0 推进时 chain 只有首帧,于是把同画面的末帧当新帧追加;(b)content_window 对单帧段做行方差,全 0 → 判成"整屏都是固定区"→ 返回负窗口(如 -1277),crop 越界。修法(已落地):选帧去掉 len(chain)>1 限制;content_window 在 seg[1]-seg[0] < 2 时按整帧返回 (H, 0, 0);long_image.py 拼接时跳过 tops[k] >= bottom_cut 的重复帧。诊断法:同组两帧直接 ImageChops.difference 看是否仅剩编码噪声(mean≈15 且无轮廓 = 同画面;注意状态栏时钟跳变会让"顶部行差异"偏大、底部行 diff 接近 0,仍属同画面)。排查要扫全链:推进量/内容窗口被"选帧 → 窗口检测 → 长图拼接"三处共用,发现一个失效点后必须一次扫完其余两处再重跑,别修一处跑一轮。单帧组的长图:静止段没有跨帧行方差可比、自测不出固定区,脚本会自动沿用同视频其余组一致的固定UI(众数、需 ≥2 组一致才外推);仅当全片都是单帧段时才需手动传 --fixed-top/--fixed-bottom。不处理则长图退化成整帧输出、多带一条输入框区(实测多 158px)。(2026-09-29 补充)⚠️ 分清两种情况:"某一段静止"是偶发故障(上一句的众数外推就是治它);"整段录屏从头到尾都静止"是另一种素材,没有可拼接的滚动内容,不属于本流程——改用 static_image.py 出逐帧/分镜长图,别拿主流程硬拼 |
| ⑧ 快滚段被误判场景切换、整段静默丢弃(2026-09-19) | 段与段之间少了一截内容,而段内衔接与长图缝验证都查不出来(各自看都"连续") | 场景切换判据是"shift≈0 且 MAE>18",但帧间位移一旦超过 shift 搜索上限(0.6 屏≈766px),搜索饱和同样返回 0 → 快速滚动被误判成切换点 → 紧随的碎片段(< min_seg)被丢弃。这是跨段盲区:报告只列段内衔接,跨段没人算。修法(已落地):丢弃前校验"上一保留段末帧 → 下一保留段首帧"的累加推进量,超出「窗口 − 100px」即把缺口区间恢复为独立段并打印告警(宁多一段不漏内容)。复核动作:跨段衔接必须用逐帧小步长累加显式算一遍,不能用两帧直接比对(坑①) |
| ⑨ selection.json 里存了相对路径(2026-09-23) | 下游 long_image.py 换一个 CWD 用绝对路径调用即 FileNotFoundError: 录屏证据_x/_raw/f_00001.png;同一批帧在主脚本刚跑完时拼接却正常 |
根因:主脚本 -o 传相对路径时,selection.json 就写成相对路径(只有 CWD = 那个父目录才解析得动)。修法(已落地):record2evidence.py 对 outdir 做 abspath;long_image.py 取帧不再信任那份路径,改为按帧号回落本目录 _raw/(dump_frames.py 本就有此回落,故没中招)。诊断法:看 selection.json 首行路径是绝对还是相对 |
⑩ 用 -vf fps=NN 抽帧抽出"插值鬼影"(2026-10-05) |
逐帧对齐曲线整体平坦无低谷(MAE 10–19),可明明静止的两帧却"差异很大";fps=120 抽出的帧数(2144)是 passthrough(636)的 3 倍多 |
录屏常是 VFR:本例容器标 r_frame_rate=120,实际时间戳间隔 1/60s、真实只有 636 帧/17.9s(≈35fps)。fps=NN 会按时间戳重采样并插出"半混合"帧 → 任何位移都匹配不上。改用 -fps_mode passthrough(或 -vsync 0)抽原始帧,每帧都是真实屏幕画面。诊断法:ffprobe -v error -select_streams v:0 -show_entries frame=pts_time -of csv=p=0 录屏 看间隔是否均匀;再比 帧数 ÷ 时长 与容器标称帧率是否吻合 |
| ⑪ 周期性卡片列表里"逐帧累加位移"必然漂移(2026-10-05) | 长图出现同一批卡片循环重复(本例每 211px 一轮、某卡片重复 3–4 次);而逐帧 delta 看着自洽(残差均值 0.006)却整段错位 | 列表卡片布局完全相同(周期 211px),帧间位移在"真值"与"真值±周期"间容易选错;几百帧累加后误差放大成整屏错位。修法:改用全景锚定(stitch_pano.py)——每帧直接在长图上全局定位、不复用前一帧结果;再加局部搜索约束 ±300px;特征升级为"分段均值⊕分段标准差"。排查要一次扫完:cap(搜索上限)必须 < 卡片周期,否则连单帧都会跳周期 |
| ⑫ 录屏含"翻页/骨架屏/内容突变",不能一条长图拼到底(2026-10-05,网页录屏三连坑) | 长图尾部出现"标题二次出现 + 卡片重影",卡片内部密布横向细线、卡片底色发灰 | 网页录屏与微信录屏的本质差异:微信从头到尾单一内容状态,网页有"骨架屏→真内容→翻页→页脚"多种状态,而本 skill 全部假设"单一状态"。三个必查点:① 起步帧必须是内容稳定帧——骨架屏帧(灰底占位)做画布初始化会把灰底污染给所有"未写入行",卡片底色全错、末尾混入骨架块(本例第2页真内容从 f_00470 才开始,338-464 全是骨架屏);② 翻页处必须分段——翻页后与前面无重叠,硬拼必错位(本例帧 338 = 分页 1/3→2/3);③ 页脚段必须从"滚到最底"的干净帧取——从拼接画布上取页脚会带入错误定位的残影("暂无可认领的 Skill"出现在页脚上方)。通用教训:拼接前先抽看首/中/末 3 帧识别内容状态,别拿第一帧就开拼 |
| ⑬b "错位线清理"切勿全局低阈值迭代(2026-10-05) | 对全图跑"行差>0.8 即修"的迭代,每轮修 568→703 条不收敛(平均化让对比转移到相邻行,边修边产生新候选),2600+ 行修改后图像发虚 | 错位线修复必须①限定区域(贴帧边界±3行,或用户圈定的瑕疵区)②阈值从紧(行差>1.5、跨行相似<2.5)③迭代上限 4 轮且要求每轮递减;不递减=判据发散,立即停。定点区域用"孤立线"判据(±2 行内无其他大差异)可收敛到 0 |
| ⑬ 长图上的"淡虚线"有多层来源,别只修缝线(2026-10-05) | 用户反复反馈"还是有浅淡的虚线" | 逐层排查发现多层叠加:① 左右边缘暗列——录屏窗口边框被拼进来,第 0 列比邻列暗 20~25(末列 19~21),深浅随帧变化 → 断续虚线感;② 1px 硬缝线——整行相对邻域偏暗 30~47;③ 贴帧边界错位线——行差小、被行内内容稀释,普通判据抓不到;④ 背景灰度条纹——纯色行灰度在 249/251/252/253 间跳变(帧间背景灰不同),单行差 2~6 级肉眼可见。修法:scripts/fix_long_image.py(边缘 3 列覆盖 → 硬缝线 dev<-18 → 边界错位线 → 定点孤立线迭代清零)。⚠ 不再做"背景统一为白"(违背保持原样,见坑⑭)。诊断顺序:先量左右边缘列均值 → 再量逐行 dev → 最后看用户截图的缩放比反推坐标 |
| ⑭ 网页悬浮(hover)造成的"卡片底色不一致"(2026-10-05) | 同一页面里某张卡片灰、其他白,用户:"原来页面是灰的就保持灰、白的保持白" | 录屏时鼠标滑过的卡片会进入 hover 态(底色变灰,本例 253→247),该帧被拼入后这个灰底就被永久固化。这不是瑕疵,是视频原貌——千万不要做"底色归一"去抹平(我做过,被用户打回)。判据:与原始帧逐行对比,原帧就是那个灰度 → 保留 |
| ⑮ 用户报"某处还有问题"时,先确认同一 UI 出现几次(2026-10-05) | 同一处"提示条/搜索框"区域反复被指出三次,前两次都白修 | 多屏录屏里标题栏/搜索栏/页脚会重复出现(本例长图中"我的 Skills+提示条+搜索框"出现两次:第1页与第2页)。定位法:① 算用户截图缩放比 = 截图内某元素高度 ÷ 长图内同元素高度;② 反推长图坐标;③ 在该坐标附近量"逐行均值+暗像素数"找异常。真凶是第 2 页顶部的交替纹(243/252 每 2~4 行交替,两帧逐行交错贴入所致)——第 1 页同位置完全干净,对比之下用户一眼看出。修法:用同页干净模板行覆盖该段 |
| ⑯ 对齐搜索取不到 s=0(2026-10-08) | 同页重复帧(实测逐像素 MAE≈0)在去重时全部漏归并;aligned 返回值比直接 shift0 算的 MAE 还大 | range(-25,26,2) 从奇数 -25 步进 2,序列是 -25,-23,…,-1,1,…,25,永远不含 0——而无位移比对恰恰最重要。修法:max_shift 取偶数(range(-26,27,2) 含 0)并加注释防回归。诊断法:aligned 值 > 直接 shift0 的 MAE,必是 0 被跳过 |
代跑提速口径(定则⑤的落地口径)
转换本身很快(实测 592×1280 / 29.7s 录屏:主脚本 44s + 长图 31s〔两组〕+ 截屏导出数秒),慢的是自己加的工序。默认按此口径,别把速度耗在周边:
- 不读源码:SKILL.md 已给 CLI 就直接跑,只在报错时读对应脚本(通读 383 行主脚本属多余开销)。
- 定量自检优先于读图:脚本已打印缝处 mean/std、总高校验、衔接量——只对 ⚠ 行读图,不逐页读小样图。读一次图 ≈ 一次多模态调用,是最大单项开销;小样图逐页过留给人。
- 已有成果先比等价、再决定复用:同目录已有 PDF/长图且视频未变时,先比对是否等价(等价判据:选中帧清单一致、PDF 体积差在 1 字节内)——等价就只补缺的那一件(如只导 PNG,数秒),不为"刷新"重跑整链(重跑 = 44s + 长图 31s)。
- 长图与复核并行:长图不依赖 PDF,可后台跑。
- 侦察一次定死路径:一次
ls拿到技能与素材的绝对路径,不要全盘find找技能或上游源目录。
素材边界(提前告知用户,不算缺陷)
- 录屏末尾滚动慢的区间:衔接必然偏大(本视频实测末段 613px)。总推进量固定时,帧间衔接之和也固定,插中间帧只会更糟——这是素材决定的,脚本已在报告中标注
- 录屏末尾静止停留(停下不动看最后一屏):该段推进 0,只出一屏(去重后 1 帧),且与相邻段的衔接极大(本视频实测 892px、重叠约 90%),内容与前一屏高度重复——属素材决定,不是重复选帧的错误
- 页面切换的过渡动画帧(半透明滑入):不算内容帧,自动剔除
- 操作型录屏没有"完整性证明"对应物:滚动型的长图证完整,操作型本质是离散页面状态、拼长图无意义——完整性由「状态关键帧报告(每帧停留时间区间)+ selection 可回溯」承载
- 混合型素材(页面切换 + App 内长列表滚动)只有口径、没有工具:page_keyframes 的稳定段内部若还在滚动,不会被拼接(它只取中位帧)——遇此素材需人工按时段切开后分别走滚动型主流程,属已知边界(2026-10-08 审查登记,待后续工具化)
- 自动选帧 ≠ 证据固定:产出为整理稿/工作快照;正式提交法院建议用帧册人工重做,并保留原始录屏载体
交付物清单(输出目录内)
录屏证据_<视频名>_一面4块.pdf # 最终排版(图片右下角顺序编号;--per-page 6 为 3列×2行省纸版式:放大3%单张6.18cm)
聊天记录完整长图.png # 由独立脚本 long_image.py 生成(证据完整性直观证明)
录屏逐帧过程长图.png # 由独立脚本 static_image.py 生成(**静止画面录屏**用;不依赖主脚本,输出目录 ~/Desktop/录屏长图_<视频名>/)
录屏关键节点分镜长图.png # 同上,--mode storyboard 形态
照片排版_<名>_一面2张.pdf # 由独立脚本 photo_layout.py 生成(聊天中保存的照片/文件证据排版)
截屏PNG/ # 由独立脚本 dump_frames.py 生成(独立截屏 PNG,课件/PPT 配图用;含 截屏清单.txt)
长截图分页.pdf + _preview.png # 由独立脚本 split_to_a4.py 生成(已有超长聊天截图分页排 A4;小样须人工过目)
选帧报告.txt # 每对相邻帧的推进/衔接量明细+备注
selection.json # 选中帧清单(可复现)
小样图复核/ # 每页6帧拼图,供人工复核
状态关键帧/ # page_keyframes.py 产出(操作型录屏:整帧PNG+状态关键帧报告.txt+状态关键帧小样.png)
_raw/ # 密集帧(复核确认后可删)
踩坑杂记
- 审查登记(2026-10-08 对抗性审查产出,两项待验证/待办):① 主脚本 record2evidence.py 抽帧用的是
-vf fps=NN(第280行附近),与坑⑩"fps=NN 插鬼影帧、必须 passthrough"的家训直接矛盾——v7 定稿时端到端实测通过说明选链对鬼影帧有容忍度(165px 容差大),但改 passthrough 会牵动 min_seg×fps 等帧数逻辑,须端到端重测后才可改;另实测--fps 8曾出现帧数不降(1580 未变)未定论,复用旧帧还是参数失效待复现。② photo_layout.py 目录收集原本会吞_开头的非素材文件(_编号小样_终审.png被排进 PDF 的风险)——已修:跳过_开头文件。 skeleton_check.py不限于录屏产物:现成的一堆截图(网页截屏、微信图片保存的图)要挑掉加载页/广告页,同样先跑它出打分表+编号小样再人工终审- caption 两个小坑(2026-10-08):①主流程帧名是
f_00001.png(带下划线),photo_layout 的FRAME_RE用f(\d+)匹配不上会回落成纯文件名——主流程专用frame_caption正则必须写f_?(\d+);②秒数格式化用:g会出「00:0」,必须:04.1f固定一位小数得「00:00.0」 -
「先问」的适用面比想象大(2026-10-08 三次教训):录屏先问拍的是什么(第 0 步);排版先问版式方案(先问后排);
--yes跳问只许在用户已明确或同方案重跑时用——凡有默认值的环节,静默落默认 = 拿机器的假设顶替用户的意图 -
中文字体注册用
/System/Library/Fonts/PingFang.ttc(TTFont),勿用 pdfmetrics 内置 CID 备用字体 static_image.py的中文字体按可用性回落:PingFang.ttc → Hiragino Sans GB → STHeiti Medium → Songti(缺字体会直接报错,不静默出方块)。2026-09-29 实测:在本会话沙箱下os.path.exists("/System/Library/Fonts/PingFang.ttc")返回 False,回落生效;若哪天主流程 PDF 里中文出方块,先查这一条- PDF 抽查渲染用 pymupdf(fitz);qlmanage 只出首页缩略图
- HEVC 录屏 ffmpeg 读取无碍;用户若在浏览器用帧册工具,HEVC 需 Safari
- 抽帧帧率经验:15fps 兼顾精度与耗时(446 帧/30s 视频约 2 分钟);滚动极快场景升 20fps
混合底色场景处置口径(灰底聊天页 + 白底文章页)
一条录屏含两种底色页面时,自动固定区检测对灰底页有效、对白底页失效(坑⑥)。按此口径处置:
- 先跑完再诊断,不预判:主脚本照常跑,再看长图整条缩略图 + 各缝 ±30px 拼图。
- 两个问题签名,指向同一根因(固定区测错):长图里固定栏/状态栏周期性重复 → 固定区被当成滚动内容(fixed 偏小);选帧报告衔接量普遍为负或推进 ≥ 窗口 → 窗口 W 虚大(同样是 fixed 偏小)。白底页报"疑似白线 ⚠"多为正常段落间背景,目检确认即可,绝不机械去裁(坑⑤)。
- 固定区精确测量法(两帧逐行差异):取同段相距较远的两帧,逐行算
np.abs(a-b).mean(axis=1),diff<3 的行属固定区,diff 跳变处即边界——据此得覆盖值(白底文章页实测 上140/下150,自动检测误报 上16/下66)。 - 修正固定区后必须重跑主脚本,不能只重拼长图:选帧推进目标 = 窗口 − overlap,依赖 W;窗口修正后选帧数会变,断档才消除。长图用同样参数重跑。
- 按组覆盖,别动好组:只对失效的组传覆盖值,格式
--fixed-top "2:140" --fixed-bottom "2:150",正常组继续走自动检测。 - 终检三件套:① 长图各缝 ±30px 目检(内容连续、无白线、无重复固定栏)② 长图整条缩略图(固定栏只出现一次、正文无断档)③ 小样图复核逐页过(人工)。总高校验差 0px 为正常口径。
- 工程细节:venv 可能缺 reportlab/pypdf;管理版 venv 跑脚本用绝对路径;多视频连续处理时输出目录按视频名分开。
并入说明(2026-10-03)
- 录屏转长截屏:原独立技能
video-to-long-screenshot已并入——其「先判滚动·再定拼法」前门与长图拼接,本技能已由long_image.py(有滚动)/static_image.py(静止)正面覆盖;踩坑知识沉淀见 references/scroll-stitch-lessons.md。 - 录屏截图净化:原独立技能
recording-screenshot-cleanup已并入——脚本scripts/cleanup.py,用法与判据见 references/recording-screenshot-cleanup.md。 - 已有长截图分页排 A4(2026-10-04):原独立技能
chat-screenshot-to-a4已并入——脚本scripts/split_to_a4.py,用法见「一条命令用法」对应段落;输入是现成图片(非录屏),与主流程互补。
依赖
ffmpeg/ffprobe、numpy、Pillow、reportlab、pypdf(WorkBuddy 管理版 venv 若缺 reportlab/pypdf,用 envs/default/bin/pip install reportlab pypdf 补装;系统 python 3.9+ 亦可)。
> 法律科技实务工具 · 维护者陆凌燕律师(北京德恒·无锡)