fix(dedup): 修复查重率恒为0%——指纹绕开降重裁剪+局部片段复用+阈值校准+3个单位bug (#1702) #1703
Reference in New Issue
Block a user
Delete Branch "fix/dedup-zero-rate-1702"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
修复 Issue #1702:查重率恒为 0%
工单:#1702
两条明显同源成片(20s vs 11s,同镜头同人物)查重率显示 0.0%。
根因(staging 真实数据回归确认)
在 staging worker 容器内用证据视频做离线校准实验,确认三层原因叠加:
random_edge_crop随机裁 2-5%(generation.py:719)。自对照实验:同帧两次随机裁剪 phash 距离 410(crop=2% 时 46)。阈值 8 必漏。指纹改用中心 90% 区域计算 pHash/直方图(降重只服务外部平台)。<8。真实分布:同源 min=8、同帧不同裁剪 4~10、异源 4 个真实视频 min=24。阈值定为 12(统一模块常量PHASH_THRESHOLD),与异源间隔 12bit,无误报空间。改动清单
P0
sample_fingerprint_timestamps():固定 1s 密集均匀采样(长视频 >30 帧自动放宽间隔),compute_fingerprint改用;动态场景检测函数保留center_crop_frame():中心 90% 裁剪,pHash/直方图基于裁剪后画面,MD5 仍基于原始帧find_duplicate_segments重构:全量距离矩阵 + 时序一致贪心对齐 → 支持局部片段复用(复用片段可出现在任意时序位置,各成独立 run);±1 邻接窗口对齐(NEIGHBOR_WINDOW=1);连续匹配门槛短视频自适应min(5, max(2, n//2));阈值比较统一<=frame_match_rate<0.3 整条硬跳过;frame_match_rate分母改min(两视频分片数);temporal_coverage为主指标,无连续片段时按匹配帧占比回退P1 确定性 bug
fingerprint.duration/1000单位错误(duration 本身是秒)→ dedup.py + dedup_helpers.py 两处删除/1000NORM_L1;_bhattacharyya_coefficient改Σ√(ab)/√(Σa·Σb)概率分布归一(旧 L2 三通道拼接算出 ~14.9,上限应为 1;单通道旧语义不变)temporal_coverage量纲修正:duration_sec * 1000(毫秒)P2
配套
_save_fingerprint_chunks语义从「有数据跳过」改为「先删旧分片再写入」——否则算法升级对存量视频永不生效POST /videos/recompute-dedup加force: bool=false参数,force=true 时已有查重数据也重新入队_is_bad_fingerprint修正:旧逻辑「所有 phash 相同即黑屏」误杀短视频(11s/5 片内容连续时 phash 天然相同)→ 要求 ≥8 帧且相同占比 ≥80% 才判坏校准数据(2026-09-05 staging worker 容器,cv2 4.10.0)
测试
tests/unit/test_dedup_1702_zero_rate_fix.py(15 用例):同源不同裁剪检出非 0、局部复用(结尾2s≈中间2s)检出、异源不误报、N=1 不回归、时长单位 bug、直方图归一化、coverage 量纲、自适应门槛、±1 邻接、force recompute、0 匹配日志查重请求 / 响应数据结构
重算接口:
POST /videos/recompute-dedup响应:
{ "enqueued": 2, "skipped": 0 }查重结果(写入 generated_videos):
duplicate_rate(0-1,融合分,全片重复判定 = fusion≥0.70 且帧比例≥0.7 或 coverage≥0.5);check_duplicate 返回duplicate_segments(query/target 起止毫秒 + avg_distance)。部署后验证
PR 合并部署 staging 后,对工单两个证据视频触发 force 重算,确认 duplicate_rate 非 0(部署发布专家跟进验证)。
Closes #1702
🚀 预览环境已部署
【阻塞级判定】
📊 审查概览
🔴 阻塞级问题(必须修复)
无
💡 改进建议(不阻塞合并)
check_duplicate方法中,当chunk_data为空时,回退逻辑构造的existing_chunk_objects使用了固定的start_time_ms: 0和end_time_ms: 0。如果存量数据只有 phash 列表而没有分片时间信息,find_duplicate_segments计算出的重复片段(DuplicateSegment)的目标时间范围将全部为 0。这会导致 API 返回的重复片段时间戳不准确(显示为 0ms-0ms)。建议确认这是否符合预期,或者考虑在回退时根据视频总时长进行简单的均分估算,以提供更有意义的时间范围信息。✅ 良好实践
find_duplicate_segments的重构逻辑非常清晰,通过构建候选列表和贪心对齐算法,有效解决了时序错位问题。center_crop_frame和_bhattacharyya_coefficient等函数中,对空值、零除、负数开方等异常情况做了妥善处理。force参数,并在算法层面保留了旧数据(JSON 字段)的回退读取逻辑,确保了平滑升级。✅ 格式检查通过 | ✅ 逻辑审查通过 | ✅ 性能良好
🤖 由 AI 代码审查机器人自动生成 | 2026-09-04 23:57:11 | 模型:
🗑️ 预览环境已清理
PR #1703 已关闭或合并,对应的预览环境已被清理。