聊天录屏转截图 PDF
概述
把一段滚动的屏幕录像(典型场景:微信聊天记录的滚动录屏)转换成一组
连续截图——下一张截图的开头包含上一张截图的结尾内容(消息不丢失、
无缺口),并按「一页 4 张、从左到右」排布合成 PDF。
核心脚本:scripts/video_to_pdf.py(依赖 opencv-python-headless、pillow、numpy;
输出 Word 需要 python-docx)。
辅助工具(可选,默认不用):scripts/make_contact_sheet.py ——把截图目录拼成
一张缩略图,用于肉眼检查相邻截图是否衔接。仅当用户明确要求"检查一下衔接"、
或有具体理由怀疑截图有缺口时才跑,不要主动跑。
工作流程
- 确认输入视频路径(mp4/mov/avi 等常见格式,微信/QQ 录屏均可)。
- 确认 Python 环境已安装依赖(见「环境准备」)。已确认过就不再查。
- 运行脚本生成 PDF 与截图目录。省略输出路径时,产物默认保存到当前工作目录(即运行命令时所在的目录,通常是你的工作区);建议在目标输出目录内运行,或显式给出输出路径。
- 用
present_files呈现 PDF 与 Word,任务即完成。
默认不做的额外步骤(除非用户明确要求):拼缩略图、逐张查亮度/空白帧、
计算 SHA-256、写归档索引。这些不是流程的一部分。
# 最简用法:省略输出路径,产物自动落到「当前工作目录」(运行命令时所在的目录)
python scripts/video_to_pdf.py <输入视频>
# 也可显式指定输出(给路径或目录均可,文件名自动派生)
python scripts/video_to_pdf.py <输入视频> <输出.pdf>
# 仅当用户明确要求检查衔接时才跑,默认跳过
python scripts/make_contact_sheet.py <输出名_frames>
执行效率(重要,务必遵守)
实测教训:一段 58 秒的录屏,脚本本身只跑 136 秒,但收尾被拆成十几轮工具调用
(拼图 → 读图 → 查亮度 → 算哈希 → 写索引),每一轮都要额外付一次模型推理往返,
总墙钟被拖到 10 分钟以上。真正干活约 3 分半,其余约 6 分钟全是"碎步跑"。
规则:
- 能合并的命令就合并,尤其那些"顺便做个检查"的收尾动作,一条命令一次跑完,
不要拆成一轮一轮;
- 不要为了自检增加步骤:拼缩略图、查空白帧、算哈希、写归档索引默认全跳过;
- 环境检查一次搞定:确认 venv 和依赖是否已装,一条命令解决,
不要把 ls / import / 读视频属性拆成三次调用;
- 脚本跑完 → present_files → 结束,中间不插额外动作;
- 不要用 Read 去"看"生成的图片来自检(既慢又无收益)。
默认行为(无需额外参数即可满足常见需求):
- 输出默认到当前工作目录:运行时省略输出路径,PDF/Word/截图目录都会生成在「运行命令时所在的目录」(即当前工作区);文件名由输入视频名自动派生(如
视频.mp4→视频.pdf/视频.docx/视频_frames/)。想输出到别处就显式给输出路径即可; - 每张截图前进「可视区高度 - 30% 重叠」,下一张开头衔接上一张结尾;
- 自动检测并排除固定顶栏(联系人名)和底栏(输入栏)对滚动检测的干扰;
- 向上滚动录制的视频(从最新翻到最早,最常见)自动反转截图顺序,
使 PDF 按「最早 → 最新」阅读;--keep-order可保持录制时间顺序; - 滚动过快时自动倍增抽帧率重试(5 → 10 → 20 fps);
- 开头交互选择抽帧率(限时 5 秒):在交互终端运行时,会提示选择
1. 每秒 5 帧(默认,速度快)/ 2. 每秒 10 帧(耗时长,滚动快),
超时或非交互环境自动走 5fps。显式传了--sample-fps或加--no-interactive
则跳过该提示; - 清晰帧优选(自动):截图点落在「滚动运动模糊」瞬间时,自动向后搜索
最近的静止清晰帧再截,避免产出模糊截图(如微信白底聊天在快速滚动时)。
默认搜索窗口 8 帧;--still-window可调,--still-thresh为静止判定阈值; - 点开图片自动单独截图:录屏中点开聊天图片全屏查看的瞬间会被检测,
单独截一张插入对应聊天截图之后(文件名带_img后缀,如
shot_004_img.png);--no-viewer可关闭该检测; - 截图 PNG 同时保存到
<输出名>_frames/目录(--frames-dir none关闭); - 同时输出 Word 版本(
<输出名>.docx,与 PDF 同排布:图片在无边框
表格中、一页 4 张从左到右、单元格内垂直居中),便于后续手动增删截图、
加批注;--no-docx可关闭;
防空白页要点:表格行高需在页面可用高度基础上预留约 3mm,表格后的
段落压缩到 1pt(Word 规范要求表格后必须有段落,占满整页会溢出成空白页); - PDF 为 A4 横放、1 行 4 列、从左到右排列。
环境准备
优先使用托管 Python 的隔离 venv(若未创建依赖,先安装一次):
# venv: C:/Users/Administrator/.workbuddy/binaries/python/envs/default
<venv>/Scripts/python.exe -m pip install -i https://pypi.tuna.tsinghua.edu.cn/simple \
opencv-python-headless pillow numpy python-docx
之后直接用 <venv>/Scripts/python.exe 运行脚本。若在其他机器上使用,
任何装有上述三个包的 Python 3.9+ 均可。
常用参数
| 参数 | 默认 | 说明 |
|---|---|---|
--overlap |
0.30 | 相邻截图重叠比例(按滚动可视区高度),调大更保险,调小截图更少 |
--grid |
row4 | 页面排布:row4=1 行 4 列从左到右;grid22=2x2 从左到右、从上到下 |
--page |
auto | 页面尺寸:a4l/a4p/a3l,默认随 grid 自动选择 |
--sample-fps |
5 | 抽帧帧率;匀速慢滚首选,滚动过快可手动加到 10~20 |
--no-interactive |
关 | 加上则跳过开头的抽帧率交互选择(默认在交互终端会提示选 5fps/10fps,限时 5 秒,超时走 5fps)。批处理/自动化场景加此参数 |
--analyze-scale |
0.25 | 滚动位移分析的降采样因子(0~1],越小越快。0.25≈2 倍于 0.33、约 10 倍于原分辨率;0.33 更稳健。最终截图仍用原分辨率、不影响清晰度;纯色/低纹理聊天背景建议调大到 0.33~0.5 |
--frames-dir |
auto | 截图保存目录;none 为不保存单张截图 |
--quality |
85 | PDF/Word 内 JPEG 质量(1-100) |
--no-docx |
关 | 加上则不生成 Word 版本(默认同时输出 .docx) |
--no-viewer |
关 | 加上则不检测「点开图片全屏查看」(默认检测并单独截图插入) |
--keep-order |
关 | 保持截图按录制时间顺序,不自动反转 |
--still-window |
8 | 清晰帧优选:位移达标后向后搜索静止清晰帧的窗口(帧数)。滚动快/截图模糊调大(12~15);纯文本聊天可调小 |
--still-thresh |
3.5 | 清晰帧优选:帧间平均像素差低于此值视为静止清晰帧。画面噪声大时适当调大 |
--start-sec |
0 | 从视频第几秒开始处理(跳过开头无关操作),如 --start-sec 24 |
--end-sec |
到结尾 | 处理到第几秒为止。配合 --start-sec 只截取录屏的某一段,长视频截短段时省掉整条解码 |
--no-pdf |
关 | 只出 Word、不出 PDF(默认 PDF 和 Word 都出) |
--mem-scale |
1.0 | 抽样帧存入内存的缩放因子(0~1]。长录屏内存吃紧时用 0.25~0.5;最终截图会回读原始分辨率,不影响清晰度 |
--mem-gray |
关 | 抽样帧以单通道灰度存入内存,内存再降到彩色的 1/3(可与 --mem-scale 叠加)。滚动分析只看结构与亮度,灰度足够 |
--no-auto-fps |
关 | 检测到滚动过快时不自动倍增抽帧率。长视频 + 内存吃紧时务必加上(否则 5→10fps 会让内存和耗时都翻倍) |
长录屏(10 分钟以上)低内存处理
内存占用 ≈ 抽样帧数 × 宽 × 高 × 通道数 × mem_scale²。抽样帧数 ≈ 视频秒数 × sample-fps。
例:17.5 分钟 / 870x1920 / 5fps → 约 5200 帧,原始分辨率需 24 GB,必须降内存。
推荐组合(8GB 内存机器实测够用):
python scripts/video_to_pdf.py <视频> <输出.pdf> \
--start-sec 24 --mem-scale 0.3 --mem-gray --analyze-scale 0.5 --no-auto-fps --no-interactive
- 峰值内存约 0.8 GB(相比原始分辨率的 24 GB);
- 模板匹配的实际工作分辨率 =
mem_scale × analyze-scale,上例为原图的 0.15 倍,
已高于此前--mem-scale 0.25彩色方案实际生效的 0.0625 倍,选点质量不降反升; - 截图仍由
read_full_frames()回读原始彩色分辨率,PDF/Word 画质完全不受影响。
只截取录屏的某一段(分多个文档输出)
# 前 26 秒
python scripts/video_to_pdf.py <视频> <输出1.docx> --start-sec 0 --end-sec 26 ... --no-pdf
# 46 秒到结尾
python scripts/video_to_pdf.py <视频> <输出2.docx> --start-sec 46 ... --no-pdf
--end-sec会在抽帧和回读两处同时生效,所以截短段时不会为 26 秒的片段去解码 16 分钟的全片;- 输出路径传
.docx时,Word 与截图目录都按它派生(<输出>_frames/); --no-pdf只跳过 PDF,Word 照常生成。
注意事项与排查
- 录制建议(可提前告知用户):单向匀速滚动效果最好;前后停顿几秒
没关系,会被自动跳过。 - 想保留聊天里的图片:录屏时点开图片、停留约 0.5~1 秒、关闭返回、
继续滚动即可——点开的全屏大图会被单独截一张,插在对应聊天截图之后。
若不需要,录屏时避免点开图片/链接等切换画面的操作(或用--no-viewer)。 - 快速甩动:相邻帧滚动超过可视区一半会导致位移估算失败(脚本会
自动倍增抽帧率重试,仍不理想时提示用户用--sample-fps 10~20重跑)。 - 抽帧率 vs 滚动速度(按需调参,勿设默认):
--sample-fps 5为默认
(匀速慢滚首选,约每秒抽 5 帧;76fps 视频≈每 15 帧抽 1 帧,速度更快)。
纯文本/极慢滚可进一步降到 3 加速(位移估算时间大致减半,截图数量不变);
快速甩动的录屏反而要调高到 10~20。判断依据:脚本相邻抽样帧的
滚动量须远小于「可视区高度的 35%」(否则触发自动翻倍重试、白抽一遍)。
这是按录屏内容调参,不建议写死成默认值。 - 截图模糊:截图点落在快速滚动的运动模糊瞬间时会产出模糊图。脚本
默认用「清晰帧优选」自动避让(向后找静止清晰帧);若仍有个别模糊图,
可调大--still-window(如 12~15)重跑。注意:运动模糊是滚动录屏的
物理产物,无法完全消除,只能尽量选到滚动停顿后的清晰帧。 - 来回报动:位移按带符号累计,小幅回滚会被抵消;若用户故意来回滚
又想保留全部内容,建议分段录制分别处理。 - 横屏/PC 端录屏:同样适用(滚动检测不依赖竖屏假设)。
- 若提示置信度不足或截图数量异常(如仅 1 张),多为视频压缩严重或
画面变化剧烈,可尝试--min-conf 0.45或提高--sample-fps。
脚本原理(维护时参考)
- 按抽帧率抽样 → 用「时间标准差」检测每行是否滚动,得到滚动可视区
[y_top, y_bot](排除固定 UI;只用聊天帧检测,看图黑底画面会污染); - 滚动区 20%/50%/80% 三条横条各自模板匹配、投票取中位数估算带符号
位移(单条匹配在纯色背景上会自信地误匹配,多票一致才采信); - 「点开图片」区段检测:场景切换进入 + 内部画面稳定 + 场景切换退出,
且退出后回到原位(或画面黑底)→ 取区段中间帧单独截图,按时间插入;
其帧不参与滚动估算; - 位移累计达到「可视区高度 − 重叠区」时截取一帧;若该帧处于滚动
运动模糊中,向后搜索窗口内最近的静止清晰帧(帧间像素差 <
--still-thresh)替代之,窗口内无静止帧则回退用当前帧;结尾剩余
内容超过可视区 25% 时补截最后一帧; - 若整体向上滚动(新→旧),反转全部截图(含看图截图)顺序使 PDF 按
旧→新阅读; - 用 Pillow 把截图按每页 4 张排入 A4 页面并保存为多页 PDF;
- 用 python-docx 生成同排布的 Word(无边框表格承载截图,可自由增删、
加批注、调整顺序)。
版本变更
- v1.0.8(2026-09-08):Word 排版修复。横版每页 4 张的 inline 流式排布中,
原img_w_limit = usable_w / per_row未预扣横向间隙与线框外扩,导致 WPS 下第 4 张
被挤到下一行。修复:宽度预算预扣(per_row-1)*gap_mm + per_row*2*0.4mm 线框 + 6mm 渲染余量,并新增_zero_run_spacing()把每个图片/间隔图 run 的字距清零,杜绝
WPS 在并排图片间插缝。每行总宽由 ~283mm 降至 267.8mm(可用宽 277mm),跨客户端
(腾讯文档预览 / Word / WPS)均稳定一行 4 张。 - v1.0.7(已废弃):曾加入交互式启动器(launcher.py + HTML),后按用户要求回退,
不对外发布。 - v1.0.6:纯命令行技能包。含
--start-sec/--end-sec/--mem-scale/
--mem-gray/--no-auto-fps/--no-pdf+ 原分辨率回读(read_full_frames)。