我的分屏修复通过了所有测试,却只修好了一半 bug
我的 AI 剪辑工具会把两位说话人上下叠成竖屏分屏。其中近三分之一的片段有部分画面重复。我的第一次修复只解决了一半。
2026年10月11日
创建于 2026 年 10 月 12 日 · 更新于 2026 年 10 月 12 日
Katto 能把长视频变成竖屏短片。它是我一个人开发的,而且整个过程都公开进行。10 月 10 日,我修复了一个分屏的布局几何 bug,测试通过,上线,然后在更新日志里写下了“已修复”。当天晚上,我发现同一个 bug 还在出现。而且就算修了两次,分屏也还谈不上好。
分屏是用来干什么的
播客或访谈通常是用远景拍的:两个人坐在桌边,在 16:9 的画面里一左一右。竖屏短视频是 9:16。直接裁中间,你只会得到一张桌子,看不到任何人的脸。跟着说话的人走,另一个人每次回应时都会从画面里消失。
当两个人坐得足够远时,Katto 会换一种做法:围绕每个人各裁一个窗口,然后上下叠放。两块半高的面板,两张脸始终都在画面里。这是双人镜头的标准解法,也就是重构图页面里介绍的功能。
问题出在哪里
每块面板都有一个宽度,代码给这个宽度设了上限,好让两个窗口不重叠。这个上限是根据两个人之间的距离算出来的。听上去合理,但量错了东西。
举一个真实的例子。源视频宽 1920 像素,两个人相距 1300 像素。算出的上限是 1235,面板宽度是 1214,所以上限根本没有生效。接着,每个窗口以各自的人物为中心,超出画面边缘的部分再被推回画面内。左边的窗口向右移了 358 像素,右边的窗口向左移了 236 像素。也就是朝彼此靠拢。
两个 1214 像素的窗口要并排放下,需要 2428 像素。而画面只有 1920。无论怎么摆,至少有 508 像素会同时出现在两块面板里。
在屏幕上看,就是上面板的底部和下面板的顶部出现同一条画面:一个肩膀出现两次,半张脸出现两次,有时整个人出现两次。
我本该一开始就从这条规则出发,一句话就能说清:两个宽度为 W 的窗口,只有在 2W 不超过画面宽度时才能并排放下。两个人之间的距离跟这件事毫无关系。
发生得有多频繁
我回头检查了 Katto 已经交付的分屏片段,直接从存储的几何数据里读出重叠量:第一个窗口的右边缘减去第二个窗口的左边缘。
其中 31% 存在重叠。在这些片段里:
| 重叠量 | 占比 | 看到的效果 |
|---|---|---|
| 300 px 及以上 | 18% | 一个人或一段上半身重复出现 |
| 150 到 299 px | 15% | 内侧边缘出现一块身体 |
| 60 到 149 px | 36% | 一条背景 |
| 60 px 以下 | 30% | 基本察觉不到 |
百分比经过四舍五入,所以加起来是 99。大致上,三分之一看不出来,三分之一不太明显,三分之一明显是坏的。最严重的就是上面那个 508 像素的例子。
我第一次统计出来的是 23%,不是 31%。我计算重叠时用到的一个字段在部分旧片段里不存在,这些行就被悄无声息地跳过了。这个数字看起来挺合理,我差点就用了它。
修复方案,以及它通过的测试
新的上限就是那条一句话的规则:每块面板最多占画面的一半,两个窗口的位置保证不可能交叉。只有当原始布局确实重叠时,它才会生效。早先的一个版本在所有情况下都重新计算几何,结果把少数本来正常的片段挪动了一两个像素。一个会碰到正常片段的修复,本身就是第二个 bug。
然后是验证。我拿一批存储的片段,分别在关闭和开启修复的情况下重新跑:所有不重叠的片段输出完全一致,发生变化的恰好就是那些重叠的片段。接着用一个真实视频跑了一个新任务:12 个分屏片段,上限在其中 3 个上生效,全部没有重叠。
于是我上线了,并在更新日志里写了已修复。
当天晚上
又跑了一个测试视频。在修复开启的情况下,有一个片段出现了 44 像素的重叠。
原来有两个函数。一个处理典型情况:两个人隔着桌子相对而坐,给每人一个近景。另一个是通用的兜底方案,在第一个函数不接手时使用:它取每一帧里最左边和最右边的脸,按这两张脸来分屏。两者都会生成分屏片段,并各自给出一对窗口位置。两者的缺陷几乎一字不差。我修了第一个,第二个连打开都没打开过。
让人懊恼的是,证据看起来非常有说服力。我的测量和测试样本里混着两条代码路径,而我完全不知道。第一次修复就把样本清理得足够干净,让结果看起来已经彻底解决。基于存储结果的测量统计的是症状,它不会告诉你有多少个地方在制造这些症状。
第二次修复就是把同一条规则放进第二个函数。在同一批样本上重跑:又有 4 个片段被纠正,56、124、230 和 340 像素的重叠全部降为零,其他一切不变。那个 56 像素的片段在早上的修复中毫发无损地漏了过去。这就是漏洞被当场抓住的样子。
检查有没有第三个
在第二次写“已修复”之前,我在代码里搜索了所有设置窗口位置的地方。不是一处,而是四处。其中两处就是上面的两个函数,现在都已修正。第三处根据一条已经找到的分割线来构建面板,并把每块面板限制在分割线各自一侧的空间内,所以从结构上它的两个窗口就不可能交叉。第四处只是把第三处的数值传下去。
所以这次我的说法范围更窄:10 月 10 日以后渲染的分屏,不会再把画面的同一部分同时放进两块面板。在此之前渲染的片段,保留当时生成时的几何布局。
还有哪些地方不对
没有重复画面只是底线,不等于好的分屏。今天在真实视频上我能看到三个问题:
面板相对于里面的人太宽了。每块面板的尺寸是按画面算的,而不是按人算的。在远景的演播室镜头里,一个人可能只占面板的三分之一,剩下的都是身后的布景。两块半空的面板,看起来可能还不如一个构图好的单镜头,这也是我还没把分屏开放给更多类型镜头的原因。让每块面板贴着人物收紧,是接下来要做的工作。
长片段里的一段简短对话不会被分屏。判断是针对整个片段做的。如果一个人讲了四十秒,另一个人插话五秒,这个片段会一直保持单一构图,回应发生在画面之外。
有些双人镜头永远不会被识别出来。触发条件是数人脸。侧脸、距离较远或光线不好时,人脸检测器可能漏掉一个人,而人体检测器在每一帧里都能看到他,于是片段退回到单一构图。
这些问题都不会造成画面重复。它们是“没坏的分屏”和“真正好的分屏”之间的差距,我宁愿把这一点说清楚,也不想让“已修复”一个词同时代表两者。
我从中记住的东西
当一个修复奏效时,去找那些产生相同输出的其他地方。不只是你发现 bug 的地方,而是所有可能产出同类结果的地方。这次只需要搜索一个字段名,而我是在上线之后才搜的,而不是之前。
一个汇总数字无法告诉你修复是否完整。它下降了很多,而恰恰是大幅下降让人停止继续寻找。
悄悄跳过数据行的指标,比直接报错的指标更糟。一个缺失的字段把 31% 变成了 23%,连一条警告都没有。
从约束本身出发,而不是从替代指标出发。上限之所以根据两人之间的距离来算,是因为那个数字就在手边。真正的限制是画面宽度,而它同样就在手边。
10 月 10 日的更新日志条目(英文)写着“已修复”,早了几个小时。现在它如实记录了发生的事情。