无标题
H.264 与“闭眼抽帧”学习笔记
在 PPT、网页操作、数据看板这类屏幕录制视频里,不一定要先把每一帧都解码成图像再判断页面变化,而是可以先观察 H.264 压缩码流里的数据包大小变化。编码器本来就已经分析过相邻画面的差异;如果画面很容易预测,压缩数据通常很小,如果画面突然大面积变化,压缩数据往往会明显变大。
所以所谓“闭眼抽帧”,不是完全不处理视频,而是在候选检测阶段不看像素画面,只看压缩数据的时间变化;等找到可能的换页位置后,再解码少量代表帧。
一、为什么这个问题可以从编码数据里找线索
DataAgent 的视频理解任务里,视频内容通常是界面操作、PPT 或类似页面讲解。这类视频有一个很明显的特点:大部分时间画面是静止的,只有在换页、弹窗、表格加载、动画出现时才发生大面积变化。
如果按固定间隔抽帧,会遇到两个问题:一是同一个静止页面会被抽出很多重复帧;二是停留时间很短但重要的页面可能被错过。传统做法是解码所有帧,然后用像素差、直方图、SSIM、感知哈希或视觉模型比较前后画面。这样当然可行,但成本比较高。
H.264 给了另一条线索。它的压缩依赖预测:能从当前画面内部或前后参考画面预测出来的内容,就不需要完整保存;预测不准的地方,才需要编码更多残差。于是,页面静止时包通常很小,页面切换时包大小会出现一簇明显上升。
可以粗略理解为:
1 | 静止页面:预测准确 -> 残差小 -> 包小 |
这个近似并不是在所有视频上都成立,但在静态 UI 和 PPT 类视频里通常很有用。
二、H.264 为什么能压缩视频
H.264,也叫 AVC,是一种视频压缩编码标准。如果每一帧都按完整图片保存,视频会非常大。它主要利用两类冗余来压缩:
第一类是空间冗余。同一张画面内部,相邻区域经常很像。比如 PPT 里有大面积白色背景,没有必要把每个白色像素都完整记录一遍。
第二类是时间冗余。相邻帧通常变化很小。比如一个页面停留 5 秒,标题、表格、背景都不动,可能只有鼠标移动了一点。编码器没必要反复保存整张页面,只要记录“哪些地方和参考画面不同”。
一帧画面的编码过程可以简化成:
1 | 原始画面 |
这里最关键的是预测和残差。预测越准,残差越小,需要保存的新信息越少;预测越失败,残差越大,压缩后的数据量也就越容易上升。换页、整屏刷新、弹出大面积内容,都会让原来的参考画面失效,这就是包大小能够反映画面变化的原因。
三、I/P/B 帧应该怎么理解
工程里经常说 I 帧、P 帧、B 帧。严格来说,H.264 中定义的是不同类型的 slice,但在抽帧讨论里,直接按帧类型理解问题不大。
I 帧的 I 是 Intra,主要靠当前画面内部的信息进行压缩,不依赖其他视频帧来恢复当前画面。它可以理解为重新描述一张相对完整的画面,但它不是未压缩图片,仍然会经过帧内预测、残差、变换、量化和熵编码。I 帧通常比较大,也常用于随机访问、拖动进度条和错误恢复。工程上常把 I 帧叫关键帧,但更严格地说,适合随机访问的通常是 IDR 帧:IDR 帧通常是 I 帧,但 I 帧不一定都是 IDR 帧。
P 帧的 P 是 Predictive,主要参考过去的画面。常见说法是 P 帧只记录“相对于上一帧发生了什么变化”,这个说法便于理解,但不够严谨,因为 P 帧可以参考不止一帧,也不一定只参考紧邻上一帧。对屏幕录制来说,如果当前页面基本没变,P 帧只需要记录很小的残差、少量运动向量或鼠标变化,数据可能只有几十或几百字节;如果页面从 A 切到 B,大量区域无法预测,P 帧也会变得很大。
B 帧的 B 是 Bi-predictive,可以使用两组参考画面做预测,常见理解是同时参考前后画面。它通常压缩率更高,但编码顺序和播放顺序可能不同。比如播放顺序是:
1 | I -> B -> B -> P |
为了让 B 帧参考后面的 P 帧,编码器可能先处理 I 和 P,再处理中间的 B 帧。
三类帧可以简单对比:
| 类型 | 主要参考信息 | 一般特点 |
|---|---|---|
| I 帧 | 当前画面内部 | 相对独立,通常较大 |
| P 帧 | 过去的参考画面 | 静止场景下通常较小,变化大时也会变大 |
| B 帧 | 两组参考画面 | 预测更充分,通常压缩率更高 |
需要注意,帧类型不是包大小的唯一原因。量化参数、码率控制、分辨率、GOP/keyint、编码器的场景切换策略、封装格式,以及一个画面被拆成多少个包,都会影响最终大小。
四、包大小里的 burst 信号
对 PPT 或 UI 视频来说,包大小时间序列通常会呈现一种很清楚的形态。
页面静止时,连续包可能是:
1 | 55 B、62 B、48 B、70 B、60 B |
它们在时间轴上贴近底部,波动很小。页面切换或大面积动画出现时,可能变成:
1 | 60 B、1200 B、6800 B、11000 B、4000 B、900 B、70 B |
这不是一根孤立的尖峰,而是一簇连续变大的包。这种连续突变可以称为 burst。换页、表格加载、元素飞入、整屏刷新,都可能形成 burst。
真正需要警惕的是孤立大包。编码器可能根据 keyint 或 GOP 设置,周期性插入 I 帧或 IDR 帧。即使画面完全没变,这类帧也可能比周围 P/B 帧大很多:
1 | 60 B、55 B、48 B、30000 B、62 B、51 B |
这根尖峰前后都很平静,更像是编码器强制插入的关键帧,而不是真实换页。真实换页更像:
1 | 60 B、55 B、1500 B、6000 B、9000 B、2000 B、80 B |
所以不能用“包大 = 换页”这种单点规则。更合理的判断是:连续出现一簇大包,前后状态发生明显变化,并且之后重新回到稳定小包,才更可能是真实页面变化。
五、“闭眼抽帧”的实际流程
传统抽帧是先解码全部视频帧,再比较图像差异:
1 | 解码全部帧 -> 比较前后画面 -> 找换页 -> 保留代表帧 |
闭眼抽帧则把前面的候选检测放到压缩码流上:
1 | 读取包大小、时间戳、帧类型 |
它不是完全不解码。因为最终要得到图片,仍然需要解码候选帧;而且 P/B 帧依赖参考画面,解码器可能还要从前面的关键位置一路解码到目标时间。性能收益主要来自三点:不对所有帧做图像比较,不把所有帧送给视觉模型,最后只保留少量高清代表帧
阈值一般应该偏向召回:宁可多保留几个候选点,也不要漏掉真正的页面。后续可以再用图像去重、OCR、版面分析或多模态模型筛掉重复帧。
六、为什么要在 burst 结束后取帧
页面切换不是瞬间完成的。比如 10 秒开始换页,过程可能是:
1 | 10.00 s:旧页面 |
如果在 burst 中间截图,很可能拿到过渡画面、模糊画面、新旧重叠画面,或者动画还没结束的半成品。因此更稳的方式是在 burst 结束、包大小重新回落后再取一张稳定帧。
但这个稳定帧的“逻辑时间戳”可以记为 burst 开始时间:
1 | burst 开始:10.00 s |
这样做的好处是和 ASR 旁白更容易对齐。讲解者往往在页面刚开始切换时说“接下来我们看这个页面”,而真正稳定的画面会晚一点出现。截图取稳定时刻,语义对齐用变化开始时刻,两者可以分开记录。
七、适用边界和工程注意点
这种方法特别适合 PPT、操作界面、屏幕讲解、数据看板这类视频,因为它们通常满足几个条件:页面长时间静止,换页时变化面积大,自然运动少,同一页面停留数秒,换页前后有清晰稳定状态。
它不适合一直在运动的视频,比如电影、足球比赛、手持自拍视频、游戏录像、高频动画、摄像机持续移动的视频。这些视频的包大小会持续波动,很难区分普通运动和真正场景切换。
另外,“包大小反映画面变化”只是一个近似。更严谨地说,在编码参数相对稳定、内容主要是静态界面的条件下,编码数据量通常与画面预测难度和变化程度有较强相关性。实际实现时最好综合使用:
- 包大小;
- 时间连续性;
- 帧类型;
- burst 形态;
- 前后是否恢复稳定;
- 相近 burst 是否属于同一页动画。
孤立的大 I 帧要过滤,同一页里的多个小动画要合并,页面稳定后再截图。这样才能避免把编码器周期性关键帧当成换页,也避免同一个页面被重复抽很多次。
八、和 ASR、版面结构一起使用
抽帧只是视频理解链路的一部分。最终送给下游模型的最好不是孤立图片,而是把代表帧、ASR 旁白和版面结构按时间轴对齐。
可以理解成:
1 | 时间 10 s:ASR 旁白 |
这样能处理一些只看画面容易误判的情况。比如某页包含很多数字和表格,看起来很重要,但旁白说“This is not the page we need.”,那么结合 ASR 后就能判断它可能是干扰页。
我对这套方法的理解是:它不是用包大小替代视觉理解,而是用 H.264 编码器留下的变化信号,先低成本缩小候选范围。真正稳定可靠的系统,还是要把压缩码流信号、少量代表帧、ASR 文本和页面结构一起用。
