引言

动画素材普遍按「一拍二」「一拍三」作画:同一张画稿连续占据 2~3 帧,交错编码的实拍素材在静止段也会产生成片重复帧。这些帧在超分与补帧流水线里是完全冗余的输入——对同样的画面重复执行推理,只消耗算力、不改变结果。实测在 12.5 秒的动画片段上重复帧占 33.1%(性能模式口径),即约三分之一的推理被浪费。

去重模块要同时满足两个互相对立的目标:

- 省算力:剔除尽量多的重复帧,减少后续超分/补帧的输入帧数;

- 不破坏时间轴:剔除只影响「算哪些帧」,绝不能改变成品的帧数、帧率、时长与音频同步。

由此产生两个工程风险:"误删"(把有区别的画面判成重复,导致画面缺失)与"时序错乱"(帧数与帧率不匹配,成品时长缩水或音画不同步)。本文的实现把「不误删、不错乱」放在「省得多」之前:漏删只损失一点性能,误删是画面缺陷

只损失一点性能,误删是画面缺陷。


LaTex部分由DeepSeek写出 本人数学不好 具体逻辑是我想出来的 具体算法是它写的

1.与初代的关系

先来捋清楚这套方案的逻辑 使用12.5秒视频测试 (视频:Mikukawa Nicetry) 原设计稿是初代版 因理论问题不公开

1.1与初代的关系

初代

实装

原因

第一阶判据

全局 MAD + 变化像素比例

全局判据 + 细块判据 + 粗块判据

全局统计会把小范围局部运动平均掉,实测 23.4% 的漏判

第三阶距离

$dfrac{D_{\text{Mink}}}{K\cdot B}$

$\dfrac{D_{\text{Mink}}}{\sqrt{K\cdot B}}$

原式分母量纲错配,阈值需重标;详见 4.3

第三阶分解

LAPACK 风格 QR(对角线可负)

改进 Gram-Schmidt(取非负对角线

对角线符号翻转会让距离突变

模式

极速 / 标准 / 完美

性能 / 完美

第三阶单独成档的收益与成本都不突出(该素材上 QR 仅额外淘汰 6/98 对),两档更便于用户判断「要不要为少误删付 3.6 倍成本」

时序平滑与回填

仅在流程图中出现,未给判据

给出完整判据、参数与映射构造

这两步是「不错乱」的保证

加速路径

CuPy / ROCm / OpenVINO 三栈

纯 CPU + 与 GPU 阶段流水线并行

见 1.3

1.2关于「不上 GPU」的取舍

初代规划把四阶算子全部映射到 GPU。实装选择纯 CPU 实现,理由是收益结构变了:去重的输入是已经落盘的 PNG 序列,判决本身不改变数据流;把判决放在 CPU 上,恰好可以与超分/补帧这类 GPU 密集阶段重叠执行(见第 7 节),端到端收益与「把判决搬到 GPU 上再与推理抢同一份算力」相比更划算,而且不引入 CuPy/ROCm/OpenVINO 这类额外的运行时依赖与部署体积。实测 296 帧素材:完美模式判决单独 18 s,与 GPU 阶段并行后整体从 82.8 s 降到 70.7 s(1.17×)。

2.问题定义

设拆帧得到序列$F=\{F_1,\, F_2,\, \dots,\, F_N\}$($N$为总帧数,帧号从 1 开始)。去重模块输出判决$$d_i\in \{\text{keep},\, \text{drop}\},\, \quad i=1\dots N$$及保留序列$K=\{F_i \mid d_i=\text{keep}\}$。要求同时满足:

  1. 筛查目标:$\lvert K \rvert$尽量小(省算力);

  2. 无损目标:存在回填映射$E:[1..N]\to [1\dots \lvert K\rvert]$,使得把超分/补帧结果按$E$展开后,成品帧数、帧率与时长与「不去重」完全一致;

保守原则:误删代价 $\gg$ 漏删代价,阈值一律向「宁可保留」一侧取。

判决只依赖相邻帧:$d_i$由$F_{i-1}$与$F_i$的比较得出($F_1$无对比对象,恒保留)。这一「仅相邻」的性质是后面按块流式判决能与整段判决完全等价的基础。

3.系统位置与两种执行路径

3.1 流水线位置

输入视频
  └─[拆帧]→ input_frames/{00000001.png … 0000000N.png}
        └─[去重判决]→ 保留帧留原地,重复帧移入 input_frames_dedup_removed/(不删)
              └─[超分]→ 只处理保留帧
                    └─[补帧]→ 只处理超分结果
                          └─[回填展开]→ 按 E 还原成 N 帧(帧率不变)
                                └─[合并/音频]→ 成品

判决阶段不做任何删除:判为重复的帧只是被移出输入目录,保留在 input_frames_dedup_removed/ 便于排查与回退。真正决定成品内容的是最后一步的回填展开。

3.2 逐帧串行路径

AnalyzeAsync(framesDir, …):目录内 PNG 按文件名字典序排序后逐帧比较。状态只在内存中保留上一帧的像素prevPixels)与前两帧的路径,空间占用与$N$无关。

3.3 按块流式路径(与 GPU 阶段并行)

AnalyzeAsync(files, …, applySmoothing:false):按给定帧清单判决。调用方(流水线)把序列切成$b$帧一块,块内清单在最前面插入上一块的末帧作为「对比帧」,从而在不复制任何帧文件的前提下,让块内判决与整段判决逐对一致。块的「定稿」(写入回填依据)比「判决」晚一块,理由见 5.2 与 7.2。

4 四阶级联判决

4.1 第一阶:像素差粗筛(全局 + 两级分块)

灰度化用 ITU-R BT.601 权重(定点整数实现):$$G =\frac{299R + 587G + 114B}{1000}$$逐像素绝对差$D(x,\, y)=\lvert G_a(x,\, y)-G_b(x,\, y)\rvert$,同时统计三个层次:

全局判据

$$\text{MAD}=\frac{1}{WH}\sum \, D(x,\, y),\qquad \text{ChangeRatio}=\frac{1}{WH}\sum \, \mathbb{I}\big (D(x,\, y)>\tau_{\text{pixel}}\big)$$

$\tau_{\text{pixel}}=10$灰阶。若 MAD 或 ChangeRatio 超阈,判为「整幅画面都变了」,直接保留。

分块判据(实装新增)

块边长$b_s=\max (8,\, \lfloor \frac{ \min (W,\, H)}{32} \rfloor )$(1080p 约 33 像素),块集$\mathcal B$覆盖全图;再以$4\times 4$个细块为一「粗块」聚合。判决条件为$$\exists \, \beta \in \mathcal B:\ \frac{1}{\lvert \beta \rvert}\sum_{(x,\, y)\in \beta} D(x,\, y) \ge \tau_{\text{block}} \quad \text{或} \quad \exists \, \gamma \in \mathcal C:\ \frac{1}{\lvert \gamma \rvert}\sum_{(x,\, y)\in \gamma} D(x,\, y) \ge \tau_{\text{coarse}}$$即「存在小范围真实运动」或「存在大范围小幅变化(渐变、慢摇)」时判为不同,保留。

为什么必须分块:全局统计对局部运动极不敏感。1080p 画面里一个只占 0.05% 面积的小元素在动,全局 MAD 只有 0.02~0.1、变化像素占比不到 0.01%,$8\times 8$的 dHash 更是完全无感。实装时的标定实测(1080p 静止 + 编码噪声素材 752 帧):被判为「全局几乎无变化」的 209 对帧里,23.4% 存在块内平均差 ≥ 2.0 的真实局部运动(最高 24,该块 59% 的像素都变了);而真正雷同的帧对,块内平均差中位仅 0.70。取$\tau_{\text{block}}=2.0$,两类样本的分离度约 3 倍。粗块判据$\tau_{\text{coarse}}=1.0$用于兜「范围大但幅度小」的变化,实测仅额外生效于 3/751 对,误伤可忽略。

分块判据不增加额外遍历:块内累加与全局累加在同一次像素扫描中完成。

4.2 第二阶:dHash 细筛

把图像按盒式采样缩到$9\times 8$灰度(每格取落在其范围的像素平均,比双线性插值便宜且更稳),对每行相邻两格比较大小得到 64 位哈希:$$h = \sum_{r=0}^{7}\sum_{c=0}^{7} \big[\,g(r,c) > g(r,c+1)\,\big]\cdot 2^{\,8r+c}$$

判决量为汉明距离$H(h_a,h_b)=\mathrm{popcount}(h_a \oplus h_b)$,阈值$\tau_{\text{hash}}$(性能 5、完美 4,64 位中)。

4.3 第三阶:QR 分解精判

对两帧分别按$B\times B=8\times 8$分块,对每块$M_k$做改进 Gram-Schmidt 正交化,只保留$R$的对角线作为块特征:$$v_k=\mathrm{diag}(R_k)\in\mathbb R^{B},\qquad k=1..K$$

距离用$p=2$的 Minkowski 距离,但归一化口径与设计稿不同:$$D_{\text{norm}}=\frac{\sqrt{\sum_{k=1}^{K}\lVert v_k^{(a)}-v_k^{(b)}\rVert_2^{2}}}{\sqrt{K\cdot B}}$$​​​

为什么改分母?

设计稿写的是$\dfrac{D_{\text{Mink}}}{(K\cdot B)}$。分子是「平方和开根号」,随特征值个数的平方根增长,而分母是特征值个数本身——1080p 下$K\cdot B \approx 259200$,结果被压到$10^{-3}$量级。换算下来阈值 0.05 等价于「平均每个特征值允许差 25 个灰阶」,而真实近似帧只差 0.5~4.5 灰阶,该阶会永远判重复、形同虚设(分辨率越高越严重)。改为除以$K\times B$后,$D_{\text{norm}}$就是「特征值的 RMS 灰阶差」,量纲直观、与分辨率无关,阈值可以直接实测标定。

取非负对角:

LAPACK 风格的 QR 每列符号可翻转,同一图像块在不同实现/不同数据次序下可能得到符号相反的对角线,使距离突变。实现的 Gram-Schmidt 对每列做两遍正交化后取剩余向量的模长,天然非负:$$r_{jj}=\Big\lVert\, m_j-\sum_{p<j}\big(q_p^{\top}m_j\big)q_p \,\Big\rVert_2,\qquad q_j=\frac{m_j-\sum_{p<j}(q_p^{\top}m_j)q_p}{r_{jj}}$$

标定实测(1080p 动画 MV 3600 帧,566 个候选帧对):$D_{\text{norm}}$分布 P25 = 0.016、P50 = 0.532、P75 = 1.479、P90 = 2.136、max = 4.53。阈值从 0.5 扫到 5.0,判重帧数恒为 102 帧不变,差别只在于送进第四阶的对数(取 1.0 可省去约 1/3 的光流调用)。因此取$\tau_{\text{QR}}=1.0$:只用来挡明显不同的帧对,把难题交给下一阶。

4.4 第四阶:Farnebäck 稠密光流终判

实现路线与 OpenCV calcOpticalFlowFarneback 同源,但不依赖 OpenCV:

  1. 金字塔:灰度化后按 pyr_scale = 0.5 构造 levels = 3 层,自粗到细求解,粗层位移按尺寸比例放大作为细层初值;

  2. 多项式展开:每像素邻域$\lvert u\rvert,\lvert v\rvert\le \text{poly\_n}/2=2$内用二次多项式$f(\mathbf x)\approx \mathbf x^{\top}A\mathbf x+\mathbf b^{\top}\mathbf x+c$拟合。6 个基函数$\varphi=[1,u,v,u^2,uv,v^2]$的加权矩用 6 组可分离相关一次算完,系数$\mathbf c=M^{-1}\mathbf t$,其中$M=\sum W\varphi\varphi^{\top}$只与窗尺寸有关,全图共用一个逆矩阵;坐标按半窗宽归一化,避免平坦区系数退化导致条件数恶化;

  3. 位移求解:第二帧视为第一帧平移$\mathbf d$后$b_2=b_1-2A\mathbf d$,故$A\mathbf d=-\tfrac12(b_2-b_1)$。把$A$与$(b_2-b_1)$在 winsize = 15 窗口内累加后解$2\times 2$(比逐像素解稳定);

  4. 迭代:每轮把第一帧的展开场按当前光流 warp 后重解,位移累加,iterations = 3

  5. 守卫:单步位移上限 16 像素;条件数守卫$\det>10^{-6}$且$\det > 10^{-3}a_{11}a_{22}$,纹理过弱或方向不可观测时本像素不更新(光流保持初值)。

终判量为位移幅度$\lVert(d_x,d_y)\rVert_2$的两个统计:$$\text{MeanFlow}=\frac{1}{\lvert\Omega\rvert}\sum_{\mathbf x\in\Omega}\lVert \mathbf d(\mathbf x)\rVert_2,\qquad\text{VarFlow}=\frac{1}{\lvert\Omega\rvert}\sum_{\Omega}\big(\lVert \mathbf d\rVert_2-\text{MeanFlow}\big)^2$$

$\text{MeanFlow}>\tau_{\text{flow}}=0.5$ px 或$\text{VarFlow}>\tau_{\text{var}}=0.1$时判为不同。前两阶已经保证两帧「看起来几乎一样」,此时任何成规模的运动都会表现为非零的平均位移或非零的位移方差。

4.5 判决表与阈值

参数

含义

性能模式

完美模式

$\tau_{\text{pixel}}$

单像素差异阈值(灰阶)

10

10

$\tau_{\text{MAD}}$

全局平均绝对差

2.0

1.0

$\tau_{\text{ratio}}$

变化像素比例

0.01

0.006

$\tau_{\text{block}}$

细块平均差

2.0

2.0

$\tau_{\text{coarse}}$

粗块平均差

1.0

1.0

$\tau_{\text{hash}}$

dHash 汉明距离

5

4

$\tau_{\text{QR}}$

QR 距离(RMS 灰阶)

1.0

$\tau_{\text{flow}}$

平均位移(像素)

0.5

$\tau_{\text{var}}$

位移方差

0.1

$L_{\min}$

时序平滑最小重复段长度

3

3

性能模式在第二阶结束:通过 dHash 即判重复。完美模式在前两阶的基础上把$\tau_{\text{MAD}}$、$\tau_{\text{ratio}}$、$\tau_{\text{hash}}$各收紧一档(更不容易在前两阶就放过近似帧),再依次过 QR 与光流两关,用更高的逐帧成本换取更低的误删率。

4.6 计算资源的渐进分配

级联的实际效果是「代价高的算子只跑在少数帧对上」。实测 296 帧动画素材(295 个帧对):

  • 性能模式:第一阶淘汰 197 对、第二阶淘汰 0 对、判重 98 对;

  • 完美模式:第一阶淘汰 197 对、第二阶淘汰 0 对、第三阶淘汰 6 对、第四阶淘汰 25 对、判重 61 对。

即第三阶与第四阶各自只作用在 98 个候选帧对上,不到总对数的三分之一;而两模式的判决差异 37 帧(98−61),正是完美模式在前两阶之外挽回的疑似误删量——代价是逐帧成本从约 17 ms 升到约 61 ms(3.6 倍)。

5 时序平滑

5.1 判据

逐帧判决是「只看相邻两帧」的局部判断,会把动画刻意的顿帧(定格 1 帧再动)误判成重复。实装规则:扫描判决序列中**连续为重复**的极大段,若其长度小于$L_{\min}$,则整段改判为保留:$$\text{run}=[s,e)\ \text{且}\ \forall i\in\text{run}: d_i=\text{drop},\qquad\lvert\text{run}\rvert < L_{\min}=3 \;\Longrightarrow\; d_i:=\text{keep},\ i\in\text{run}$$

注意平滑只做「撤销删除」一个方向,永远不会把保留帧改判为删除——这保证了它不会引入新的误删,也保证了「判决表覆盖的保留帧集合」只会变大。

5.2 与按块流式判决的配合

按块流式判决时,块末尾的重复段要看到下一块才知道真实长度,因此块内不做平滑(applySmoothing:false),由调用方在掌握足够长的前缀后统一处理。一块前瞻是充分的

引理 1:设块大小$b\ge L_{\min}$,判决序列中以$s$起始的极大重复段$R$与块$j$相交。若在释放块$j-1$时已获得覆盖到块$j$末尾的全部判决,则按该已知前缀计算出的$\lvert R\rvert$与全序列一致,或$\lvert R\rvert\ge L_{\min}$从而撤销结论与全序列相同。

证明:若$R$完全落在已知前缀内,长度显然一致。否则$R$越过前缀边界,则$R$必然完整包含块$j$的全部$b$帧(前缀已覆盖块$j$),故$\lvert R\rvert\ge b\ge L_{\min}$,无论后续如何延长都不会被撤销。

实装取块大小下限 32(UI 建议 64~600),远大于$L_{\min}=3$,引理条件自然成立。

6 无损回填

6.1 回填映射

判决完成后构造回填表(长度$N$,1 基):$$E[k]=\max\big(1,\ \#\{i\le k \mid d_i=\text{keep}\}\big)$$

含义是「原始第$k$个槽位应取保留序列里的第$E[k]$帧」。重复帧在展开时复用其前一个保留帧,等价于「保持画面不动」,这正是原片在该槽位的实际内容。表随判决一起落盘(dedup_map.json),支持中断续跑。

6.2 无补帧:逐帧搬运

超分后得到的帧序列就是保留序列本身,展开即按$E$逐帧复制:输出第$k$帧 = 保留序列第$E[k]$帧。输出总帧数$=N$,帧率沿用原帧率,时长不变。

6.3 补帧:整块搬运

补帧把每个输入帧扩展成$m$个输出帧($m$为补帧倍率,如 ×2)。此时不能按「一帧换一帧」的方式展开,必须整块搬运——因为 RIFE 会把「缺号的输入」重排成连续帧号。实测规律(输入编号 1/3/5 三帧、-n 6):输出为 1..6,其中$\text{out}_1=\text{in}_1,\ \text{out}_3=\text{in}_2,\ \text{out}_5=\text{in}_3$,
即保留序列第$j$帧固定落在输出的第$(j-1)m+1$起的连续$m$帧里。

因此展开规则为:原始槽位$k$取保留序列第$E[k]$帧,其输出块为$[\,(E[k]-1)m,\ E[k]\cdot m\,)$,写入目标区间$[\,(k-1)m,\ k\cdot m\,)$。若按单帧搬运,成品帧数会只有应有值的$\dfrac{1}{m}$,时长随之缩水。

6.4 定格修正

动画里的「定格保持」在去重后表现为:展开表中相邻两个槽位取值相同(后一槽位被去重)。这类槽位在原片里是画面不动,插值结果也应当是同一帧连续$m$次;若放任补帧去取「跨过重复帧的插值帧」,会把定格变成一格渐变。实装据此判定:$$E[k+1]=E[k] \;\Longrightarrow\; \text{槽位 }k+1\text{ 的 } m \text{ 帧全部取输出块的第 }1\text{ 帧}$$

该判据不需要额外存表,直接从展开表读出。

6.5 一致性约束

回填正确的前提是三者一致:判决表(保留了哪些帧)、磁盘(输入目录里实际还有哪些帧)、超分产物(实际算出了多少帧)。实装用一条不变式守它:$$\#\{\text{超分产物}\}=\#\{i\mid d_i=\text{keep}\},\qquad\#\{\text{补帧产物}\}=m\cdot\#\{i\mid d_i=\text{keep}\}$$

任何一项不满足,本次直接放弃流水线产物、整段回退到逐阶段串行重跑,绝不带着对不上的表去做回填。

6.6 搬移失败的处理

把重复帧移出输入目录时会遇到文件被占用(图片预览解码、杀毒实时扫描、资源管理器缩略图)。实装策略是先重试,再退让:单帧搬移按 120/240/360/480 ms 退避重试 4 次,仍失败则改判为「保留」,并让这一帧一起进入超分——宁可少省一帧,也要保证「判决表说的保留集合」与「磁盘上实际存在的帧」严格一致。搬移数量与失败数都会写日志

7 工程实现

7.1 遍历与内存

判决全程只在内存保留上一帧的 BGRA 像素(prevPixels)与两张路径字符串,空间复杂度$O(W\cdot H)$,与序列长度无关。第四阶在一对帧上会额外分配若干$O(W\cdot H)$的浮点缓冲,用后即弃。

7.2 按块流式判决

为让判决与 GPU 阶段并行,判决被改造成流水线的第一级:

[去重判决 CPU] → srQueue → [超分 GPU] → ifQueue → [补帧 GPU]
  • 块间状态携带:第$j$块的判决清单在头部插入第$j-1$块的末帧(对比帧)。因为判决只依赖相邻两帧,块内判决与整段判决逐对一致,且不需要为每块复制帧文件。对比帧自己的判决被丢弃(它属于上一块)。

  • 定稿比判决晚一块:第$j$块的判决要用到第$j+1$块的判决(引理 1),因此第$j-1$块的「定稿」(写入判决表、移交保留帧给超分、把重复帧移出)发生在第$j$块判决完成之后。

  • 发布即定稿:每块定稿时把该块的判决写入定稿表,此后不再重算。早期版本在末尾拿全量原始判决再统一做一次时序平滑,结果与逐块定稿的结论出现级联差异(搬移失败改判保留会拆开重复段,使相邻段在二次平滑中被额外撤销,实测差 6 帧),被一致性校验拦下并整段回退,白白跑了一遍。改为「发布即定稿」后两者天然一致。

  • 失败恢复:任一块失败、定稿不完整、或产物数量对不上时,除回退串行外,还要把已经移出输入目录的重复帧搬回原位。否则串行路径会用「帧数够不够」判断拆帧是否完整,少了几十帧会被判成「拆帧不完整」从而跳过去重,接着超分只吃到保留帧、补帧却仍按原始帧数要结果——实测出现 235 帧输入被要求产出 592 帧,RIFE 硬凑出的后段全是废帧。

7.3 进度与三联预览

判决过程中推送三联预览:左「判决帧」(最近一次被判为重复的帧,只在判出重复时更换)、中「对比帧」(与当前帧作比较的上一帧)、右「筛选帧」(正在判决的帧)。三张图的解码与节流各自独立,且都放后台线程——这是上一版两次翻车的教训:共用节流会让一张图不动,在 UI 线程解码会直接卡死界面。


8 复杂度与资源占用

阶段

逐帧操作

时间复杂度

作用范围

空间

第一阶

像素差 + 分块累加 + 变化计数(单次遍历)

$O(WH)$

全部帧对

$O(WH)$(分块累加数组,约$\dfrac{WH}{1024}$项)

第二阶

盒式采样 + 64 位哈希 + 汉明距离

$O(WH)$

通过第一阶的帧对

$O(1)$

第三阶

$K=\lfloor W/8\rfloor\lfloor H/8\rfloor$块 × 两次 Gram-Schmidt(含二次正交化)

$O(KB^3)=O(WH\cdot B^2/64)$系数,$B=8$

通过前两阶的帧对

$O(B^2)$复用缓冲

第四阶

3 层金字塔 × 3 次迭代 × (多项式展开$O(P^2)$+ 窗口积分$O(\text{Win}^2)$

$O\big(L\cdot WH\cdot(P^2+\text{Win}^2)\big)$,$L{=}3,P{=}5,\text{Win}{=}15$

通过前三阶的帧对

$O(WH)$若干缓冲

判决阶段的总成本由通过第一阶的帧对数决定。第一阶用一次遍历、三种统计把绝大多数「明显不同」的帧对挡在外面(实测 296 帧素材上挡掉 197/295 = 66.8%),因此级联的收益来自「贵的算子只跑在少数帧对上」这一结构。

实测吞吐(640×360,含 PNG 解码与搬移):性能模式 296 帧约 5 s(≈17 ms/帧),完美模式约 18 s(≈61 ms/帧)。成本随分辨率上升,主要来自第四阶的光流求解与 PNG 解码。

9 实验

9.1 环境与素材

  • 硬件:AMD Radeon RX 580 + Windows 11 Pro for Workstations 25H2(Build 26200);临时目录位于固态盘

  • 素材 A:动画片段 12.5 s / 24 fps / 640×360 / H.264 CRF16 → 296 帧(本文主要实验素材)

  • 素材 B:同源动画片段 6 s → 140 帧

  • 素材 C:合成彩条测试视频 300 帧(用于验证「零剔除」路径)

  • 判决模块为纯 CPU 实现,判据本身与 GPU 型号无关;9.5 的端到端耗时与显卡相关

  • 判决耗时取自日志中「帧去重开始 → 帧去重完成」的时间差,含 PNG 解码与重复帧搬移,不含进程启动与拆帧

9.2 判决效果

素材

模式

判决耗时

剔除帧数

去重率

A(296帧)

性能

5 s

98

33.1%

A(296帧)

完美

18 s

61

20.6%

B(140帧)

性能

3 s

30

21.4%

C(300帧,合成)

性能

3 s

0

0%(无重复,符合预期)

素材 C 是逐帧变化的合成测试图,剔除 0 帧说明判据不会在「每帧都不同」的素材上产生误删。

分阶淘汰量(素材 A):第一阶两模式均淘汰 197 对;第二阶均 0 对;完美模式第三阶再淘汰 6 对($D_{\text{norm}}$样本最大值 1.66,阈值 1.0)、第四阶再淘汰 25 对(MeanFlow 样本最大值 2.095 px,阈值 0.5)。

记账说明:完美模式的分项之和为 197 + 0 + 6 + 25 + 61 = 289,加首帧(无对比对象)1 帧共 290,与总帧数 296 相差 6。这 6 帧是被级联判为重复、随后由时序平滑(第 5 节)撤销的孤立重复段——即级联判重 67 帧、平滑撤销 6 帧、实际剔除 61 帧。性能模式无此差额(197 + 98 = 295),说明该素材上短重复段恰好只出现在完美模式更灵敏的判决序列里,也侧面验证了时序平滑确实在起作用。

两模式差异:完美模式比性能模式多保留 37 帧(98−61),即性能模式会把 37 帧(占素材 12.5%)当成重复剔除。这 37 帧正是前两阶无法区分、需要 QR 与光流才能确认的近似帧——完美模式的价值就在这里,代价是判决成本 3.6 倍。

9.3 阈值标定

  • $\tau_{\text{block}}=2.0$:1080p 静止+噪声素材 752 帧上,真实局部运动样本的块内平均差最高 24、命中率 23.4%;真雷同帧对块内平均差中位 0.70。取 2.0 时两类分离度约 3 倍。

  • $\tau_{\text{coarse}}=1.0$:同上素材仅额外生效于 3/751 对。

  • $\tau_{\text{QR}}=1.0$:1080p 动画 MV 3600 帧、566 个候选帧对,$D_{\text{norm}}$的 P50 = 0.532、P90 = 2.136、max = 4.53;阈值 0.5→5.0 扫描判重帧数恒为 102 帧,取 1.0 可省约 1/3 光流调用。

  • $\tau_{\text{flow}}=0.5,\ \tau_{\text{var}}=0.1$:沿用 Farnebäck 双帧运动估计的经典取值区间。

9.4 无损性验证

验证判据:同一素材、同一勾选组合下,比较按块并行逐帧串行两条路径产出的回填展开帧,逐帧比对 MD5(两条路径的判决表也必须一致)。这样做的意义在于——串行路径是长期在用、已被反复验证的实现,而按块并行是本次新增、最容易出错的实现;两者产物逐帧相同,即可把「按块判决 + 一块前瞻定稿 + 整块搬运回填」这一整条新链路的正确性,等价归约到串行路径上。

素材

去重配置

执行路径对比

展开后帧数

逐帧 MD5 差异

A(296帧)

性能(剔除 98 帧)

串行 vs 并行(块 64)

592

0

A(296帧)

完美(剔除 61 帧)

串行 vs 并行(块 64)

592

0

A(296帧)

完美(剔除 61 帧)

串行 vs 并行(块 240)

592

0

合成素材(300 帧)

关闭

串行 vs 并行(块 100)

600

0

B(140帧)

关闭

串行 vs 并行(块 32)

280

0

B(140帧)

性能(剔除 30 帧)

串行 vs 并行(块 32)

280

0

两条路径的判决侧也一致:在 140 帧素材上逐项核对过,并行路径剔除的文件名集合与串行完全相同;展开帧逐帧相同本身也蕴含了两条路径的有效保留集合一致(保留集合一旦不同,展开结果必然不同)。此外并行路径的判决表在采信前还要过 7.2 的三条不变式校验,产物数量对不上时不会被使用。

时间轴不变性由构造保证并在实验中逐项核对:展开后帧数恒为$N\cdot m$($m$为补帧倍率),帧率取补帧后帧率或原帧率,因此成品时长与音频对齐与不去重时一致。内容层面的「与不去重结果等价」来自 6.3 与 6.4 的设计——被剔除的帧在展开时按「保持画面不动」回填,其内容即原片该槽位的真实内容。

9.5 端到端性能

判决与 GPU 阶段并行后(素材 A,超分 ×2 + 补帧 ×2 + 合并):

去重模式

判决耗时

逐帧串行

按块并行(块 64)

提速

性能

5 s

58.6 s

56.5 s

1.04×

完美

18 s

82.8 s

70.7 s

1.17×

去重关闭时没有 CPU 侧工作可重叠,并行只带来分块复制的额外开销:在 300 帧合成素材上实测串行 74.7 s、并行 77.7 s(−4%)。因此「不开去重就不要开并行」本身就是一条工程结论。

上限由「1 + 判决耗时 ÷ 其余总耗时」决定:判决越慢(完美模式)、素材越短,可藏进 GPU 空档的比例越高。反过来,一盘 7000 帧 1080p 的素材判决约 97 s、GPU 阶段约 3.8 h,提速只有约 1.007×。这也解释了为什么判决必须便宜——它省的是分母(要算的帧数),而不是自己。

10 局限与未来工作

  1. 仅相邻帧比较:突变镜头内的重复检测、以及「A→B→A」式的循环重复无法覆盖。扩展到多帧窗口需要引入帧索引结构,成本上升明显。

  2. 阈值是全局常数:目前按模式固定;设计稿提出的按内容类型自适应(实拍/动画/老片)尚未实装。可行路径是用前若干帧的判决统计在线估计噪声底,再据此缩放$\tau_{\text{block}}$与$\tau_{\text{MAD}}$。

  3. 完美模式的成本:光流是主要开销(18 s 中约三分之二)。可以用判决的时空局部性优化——只对「前一帧被判重复」的位置附近做光流,而不是全图。

  4. GPU 化仍未做:判决目前是纯 CPU。若未来判决要脱离流水线独立跑(例如离线批量预处理),设计稿的 CuPy/OpenVINO 路径仍有价值;但在「与推理抢同一张卡」的前提下,CPU 实现更合理。

  5. 固定阈值对极高噪声素材:$\tau_{\text{block}}=2.0$的分离度建立在编码噪声较低的前提上;高压缩老片可能出现噪声底抬升,需要用第 2 点的自适应阈值兜。

11 结论

本文给出了重复帧去除的完整实现:四阶级联判决把计算资源按「代价递增、候选递减」分配,一次遍历的像素差分块统计挡掉三分之二的帧对,QR 距离与 Farnebäck 光流只在少数候选帧对上做精判;时序平滑撤销孤立重复段以避免吃掉动画的刻意顿帧;回填映射把剔除后的保留序列无损还原为原始帧数,补帧模式下按倍率整块搬运并对定格槽位做专门修正。三条不变式(判决表 ⇔ 磁盘帧数 ⇔ 超分产物帧数)与三条失败恢复路径保证「宁可少省、不可错删错乱」。

实测在 296 帧动画素材上去重率 33.1%(性能模式)/ 20.6%(完美模式),五种配置下展开帧逐帧 MD5 与不去重路径完全一致;判决与 GPU 阶段并行后,完美模式端到端提速 1.17×,而判决本身把超分/补帧的输入减少了 20%~33%,这部分收益直接体现在 GPU 时间上。


参考文献

[1] 本文档同时引用本仓库 论文1.txt:视频帧去重算法设计与 GPU 加速实现——一种可配置四阶级联判决框架(设计稿)。

[2] Singh, A., Khan, M.Z., Sharma, S., Debnath, N.C. Perceptual Hashing Algorithms for Image Recognition. Proceedings of AISI 2025, 238:90-101.

[3] Loukhaoukha, K. Frame duplication forgery detection and localization based on QR decomposition and Minkowski distance. Journal of Forensic Sciences, 2025, 70(4):1359-1374.

[4] Farnebäck, G. Two-Frame Motion Estimation Based on Polynomial Expansion. Scandinavian Conference on Image Analysis (SCIA), 2003.

[5] Ali, M.M., Hamza, H.M., Ghali, N.I., Hosny, K.M. Video Duplication Forgery Detection Using EfficientNetB0 with Motion-Based Verification. International Journal of Computers and Informatics, 2026, 10:139-151.

[6] OpenCV. Optical Flow(calcOpticalFlowFarneback)Documentation.

[7] PlainsightAI. filter-frame-dedup. GitHub.

[8] 帧去重实现代码:src/Easy4K/Services/FrameDedupService.cssrc/Easy4K/Services/FarnebackFlow.cssrc/Easy4K/Services/ProcessingOrchestrator.cs