feat(dedup): 动态抽帧 + 滑动窗口时序匹配 (#1659) #1673

Merged
xiaoxia merged 3 commits from feature/1659-dynamic-keyframe-sliding-window into develop 2026-09-03 23:16:51 +08:00
Owner

Issue #1659: 动态抽帧 + 滑动窗口时序匹配

改动概要

1. 动态抽帧策略 — detect_keyframe_timestamps()

  • 降采样到 320×240 逐帧灰度差异检测场景切换(阈值 30)
  • 最小间隔 1s 过滤,保留差异最大的候选帧
  • 数量裁剪到 [5, 30]
  • 长视频(>3 分钟)每 30 秒分段保底至少 2 帧

2. 滑动窗口时序匹配 — find_duplicate_segments()

  • 逐帧最佳匹配 → 连续 run 检测(允许 max_gap=2 间隙)
  • 最少 5 帧连续匹配才报告
  • 返回 DuplicateSegment(query/target 时间范围 + 平均汉明距离)

3. 查重算法升级

  • 均值距离 → 中位数距离(抵抗异常值)
  • 新增帧匹配比例条件(≥70% 帧匹配才通过)
  • Bhattacharyya 系数替代余弦相似度
  • pHash + 直方图加权融合(0.7/0.3 权重)
  • 判定重复后附加 duplicate_segments 字段

4. 删除旧代码

  • 移除固定间隔常量(SHORT_VIDEO_CHUNK_SEC 等)和 compute_chunk_interval()
  • 移除 _average_histogram_similarity()

5. 测试

  • 新增 test_dedup_v2.py: 34 个测试覆盖全部新功能
  • 更新已有测试适配新算法

关键文件

  • apps/worker/video_processing/dedup.py — 核心改造
  • tests/unit/test_dedup_v2.py — 新增测试
  • tests/unit/test_dedup_engine.py — 更新断言
  • tests/unit/test_dedup_pure.py — 替换直方图测试
  • tests/unit/test_duplicate_rate.py — 更新期望值
  • tests/unit/test_fingerprint_chunks.py — 移除旧常量测试
## Issue #1659: 动态抽帧 + 滑动窗口时序匹配 ### 改动概要 **1. 动态抽帧策略 — `detect_keyframe_timestamps()`** - 降采样到 320×240 逐帧灰度差异检测场景切换(阈值 30) - 最小间隔 1s 过滤,保留差异最大的候选帧 - 数量裁剪到 [5, 30] - 长视频(>3 分钟)每 30 秒分段保底至少 2 帧 **2. 滑动窗口时序匹配 — `find_duplicate_segments()`** - 逐帧最佳匹配 → 连续 run 检测(允许 max_gap=2 间隙) - 最少 5 帧连续匹配才报告 - 返回 `DuplicateSegment`(query/target 时间范围 + 平均汉明距离) **3. 查重算法升级** - 均值距离 → 中位数距离(抵抗异常值) - 新增帧匹配比例条件(≥70% 帧匹配才通过) - Bhattacharyya 系数替代余弦相似度 - pHash + 直方图加权融合(0.7/0.3 权重) - 判定重复后附加 `duplicate_segments` 字段 **4. 删除旧代码** - 移除固定间隔常量(SHORT_VIDEO_CHUNK_SEC 等)和 `compute_chunk_interval()` - 移除 `_average_histogram_similarity()` **5. 测试** - 新增 `test_dedup_v2.py`: 34 个测试覆盖全部新功能 - 更新已有测试适配新算法 ### 关键文件 - `apps/worker/video_processing/dedup.py` — 核心改造 - `tests/unit/test_dedup_v2.py` — 新增测试 - `tests/unit/test_dedup_engine.py` — 更新断言 - `tests/unit/test_dedup_pure.py` — 替换直方图测试 - `tests/unit/test_duplicate_rate.py` — 更新期望值 - `tests/unit/test_fingerprint_chunks.py` — 移除旧常量测试

🚀 预览环境已部署

项目 详情
PR号 #1673
预览链接 https://pr-1673.preview.xiaoxiajianji.com
API环境 staging

💡 预览环境使用 staging API 数据,请勿在预览环境中操作重要数据。

🔄 每次提交新代码后预览环境会自动更新。

🗑️ PR 关闭或合并后,预览环境会自动清理。

🚀 **预览环境已部署** | 项目 | 详情 | |------|------| | PR号 | #1673 | | 预览链接 | [https://pr-1673.preview.xiaoxiajianji.com](https://pr-1673.preview.xiaoxiajianji.com) | | API环境 | staging | > 💡 预览环境使用 staging API 数据,请勿在预览环境中操作重要数据。 > > 🔄 每次提交新代码后预览环境会自动更新。 > > 🗑️ PR 关闭或合并后,预览环境会自动清理。
xiaoxia added 1 commit 2026-09-03 23:00:59 +08:00
feat(dedup): 动态抽帧 + 滑动窗口时序匹配 (#1659)
CI/CD Pipeline / Check push changed paths (pull_request) Has been skipped
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 1s
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 2s
CI/CD Pipeline / Frontend Lint (pull_request) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 22s
CI/CD Pipeline / PR Build Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 9s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been skipped
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been skipped
CI/CD Pipeline / Unit Tests (pull_request) Successful in 1m22s
CI/CD Pipeline / Integration Tests (pull_request) Successful in 1m25s
AI Code Review / AI Code Review (pull_request) Failing after 1m50s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 2m39s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 2m44s
CI/CD Pipeline / Validate - Style (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Security (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Has been cancelled
CI/CD Pipeline / Build Production API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Has been cancelled
CI/CD Pipeline / Canary Release to Production (pull_request) Has been cancelled
CI/CD Pipeline / CI Gate (pull_request) Has been cancelled
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been cancelled
fac80b1f77
1. 动态抽帧策略 — detect_keyframe_timestamps()
   - 降采样到 320x240 逐帧灰度差异检测场景切换
   - 最小间隔过滤(保留差异最大的候选帧)
   - 数量裁剪到 [MIN_KEYFRAMES=5, MAX_KEYFRAMES=30]
   - 长视频(>3分钟)每 30 秒分段保底

2. 滑动窗口时序匹配 — find_duplicate_segments()
   - 逐帧最佳匹配 → 连续 run 检测(允许 MAX_GAP=2 间隙)
   - 最少 MIN_CONSECUTIVE_MATCHES=5 帧才报告
   - 返回 DuplicateSegment(query/target 时间范围 + 平均距离)

3. 查重算法升级
   - 均值距离 → 中位数距离(抵抗异常值)
   - 新增帧匹配比例条件(match_ratio >= 0.7)
   - Bhattacharyya 系数替代余弦相似度
   - pHash + 直方图加权融合(0.7/0.3)
   - 判定重复后附加 duplicate_segments 字段

4. 删除旧代码
   - 移除 SHORT_VIDEO_CHUNK_SEC/LONG_VIDEO_CHUNK_SEC 固定间隔
   - 移除 compute_chunk_interval()
   - 移除 _average_histogram_similarity()

5. 测试
   - 新增 test_dedup_v2.py: 34 个测试
   - 更新 test_dedup_engine.py/test_duplicate_rate.py/test_dedup_pure.py
   - 清理 test_fingerprint_chunks.py 中旧常量测试
xiaoxia force-pushed feature/1659-dynamic-keyframe-sliding-window from 1418e08fa8 to fac80b1f77 2026-09-03 23:00:59 +08:00 Compare
auto-approve-bot added 1 commit 2026-09-03 23:04:15 +08:00
style: auto-format with black + isort + prettier [skip ci-format-check]
CI/CD Pipeline / Check push changed paths (pull_request) Has been skipped
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 2s
CI/CD Pipeline / Build Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 2s
CI/CD Pipeline / Frontend Lint (pull_request) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 22s
CI/CD Pipeline / PR Build Web Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been skipped
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 10s
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been skipped
CI/CD Pipeline / Unit Tests (pull_request) Successful in 1m20s
CI/CD Pipeline / Integration Tests (pull_request) Successful in 1m27s
CI/CD Pipeline / Validate - Style (pull_request) Failing after 1m33s
AI Code Review / AI Code Review (pull_request) Failing after 2m11s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 2m19s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 2m43s
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Successful in 4m35s
CI/CD Pipeline / Validate - Security (pull_request) Has been cancelled
CI/CD Pipeline / Build Production API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Has been cancelled
CI/CD Pipeline / Canary Release to Production (pull_request) Has been cancelled
CI/CD Pipeline / CI Gate (pull_request) Has been cancelled
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 2m29s
a0d4f6e111
xiaoxia added 1 commit 2026-09-03 23:09:28 +08:00
fix(dedup): ruff lint 修复 — 未使用变量 + zip strict + 冗余 import (#1659)
CI/CD Pipeline / Check push changed paths (pull_request) Has been skipped
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 3s
CI/CD Pipeline / Build Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 4s
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Validate - Style (pull_request) Successful in 1m20s
CI/CD Pipeline / Frontend Lint (pull_request) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 10s
CI/CD Pipeline / PR Build Web Image (pull_request) Has been skipped
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 2m26s
CI/CD Pipeline / Integration Tests (pull_request) Successful in 1m0s
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 12s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been skipped
AI Code Review / AI Code Review (pull_request) Failing after 2m34s
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been skipped
CI/CD Pipeline / Unit Tests (pull_request) Successful in 1m17s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 2m48s
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Successful in 4m4s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 4m26s
ACR Cleanup / ACR Image Cleanup (pull_request_target) Successful in 5s
Preview Cleanup / Cleanup Preview Environment (pull_request) Successful in 1m17s
CI/CD Pipeline / Validate - Security (pull_request) Successful in 22m37s
CI/CD Pipeline / Build Production Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Deploy Production (pull_request) Has been skipped
CI/CD Pipeline / CI Gate (pull_request) Successful in 4s
CI/CD Pipeline / Canary Release to Production (pull_request) Has been skipped
CI/CD Pipeline / Production Browser E2E (pull_request) Has been skipped
db9ee89ffa
Collaborator

【阻塞级判定】

  • 是否存在阻塞级问题:是
  • 阻塞级问题数量:3 个

📊 审查概览

  • 整体评价:需修改
  • 建议级问题数量:4 个

🔴 阻塞级问题(必须修复)

  1. [apps/worker/video_processing/dedup.py: 130-160] 严重的性能回归:全帧扫描

    • 问题类型:性能问题
    • 问题描述:detect_keyframe_timestamps 函数中使用了 while True: cap.read() 逐帧遍历视频,并对每一帧进行 resizecvtColordiff 计算。对于长视频(如1小时视频,约21.6万帧),这将导致极高的CPU消耗和处理时间,极易导致Worker超时或OOM。旧代码采用固定间隔采样,新代码改为全帧扫描未做降采样处理。
    • 修改建议:在循环中增加步长,例如每5帧或每0.5秒处理一次,避免处理每一帧。
  2. [apps/worker/video_processing/dedup.py: 130-160] 逻辑错误:遗漏视频首帧

    • 问题类型:逻辑bug
    • 问题描述:在 detect_keyframe_timestamps 中,prev_gray 初始化为 None,只有在 prev_gray is not None 时才计算差异并添加候选关键帧。这意味着视频的第一帧(timestamp=0)永远不会被添加到 candidates 中。如果视频开头就是重要场景,该场景会被遗漏,除非后续通过 min_frames 均匀补充逻辑强行加入(但这仅在总数不足时触发)。
    • 修改建议:在循环开始前,显式将第一帧(timestamp=0)加入 candidates,或者在循环内部针对第一帧做特殊处理。
  3. [apps/worker/video_processing/dedup.py: 360] 逻辑错误:重复片段平均距离计算偏差

    • 问题类型:逻辑bug
    • 问题描述:在 find_duplicate_segments 函数计算 avg_dist 时,是对 startend 范围内的所有帧(包括不匹配的 gap 帧)的汉明距离求平均。由于不匹配的帧距离通常很大(> threshold),这会显著拉高 avg_distance,导致返回的重复片段相似度指标失真,无法反映真正匹配部分的相似度。
    • 修改建议:计算 avg_dist 时应只统计 is_matchTrue 的帧的距离,或者确保分母只统计匹配帧数。

💡 改进建议(不阻塞合并)

  1. [apps/worker/video_processing/dedup.py: 620-621] 魔法数值未使用常量

    • 具体内容:在 check_duplicatecheck_batch_duplicatecompute_duplicate_rate 中计算 combined_score 时,直接使用了 0.70.3,而文件顶部已定义了 PHASH_WEIGHTHISTOGRAM_WEIGHT 常量。应统一使用常量以保持一致性。
  2. [apps/worker/video_processing/dedup.py: 593-640] 代码重复

    • 具体内容:check_duplicatecheck_batch_duplicate 方法中存在大量重复的相似度计算逻辑(计算 min_distances、match_ratio、median_distance、hist_similarity、combined_score)。建议提取一个私有方法 _calculate_combined_similarity 来复用逻辑。
  3. [apps/worker/video_processing/dedup.py: 138] 性能优化建议

    • 具体内容:在 detect_keyframe_timestampswhile 循环中,使用 cap.get(cv2.CAP_PROP_POS_MSEC) 获取时间戳。频繁调用 cap.get 在某些后端(如FFmpeg)上开销较大。建议通过帧索引计数器手动计算时间戳:current_timestamp = frame_idx / fps
  4. [apps/worker/video_processing/dedup.py: 630-640] 兼容性数据处理

    • 具体内容:当 chunk_data 为空(旧数据)时,构造的 existing_chunk_objectsstart_time_msend_time_ms 均为 0。这会导致 find_duplicate_segments 返回的重复片段中目标时间戳为 0,信息不准确。建议在无法获取时间戳时,根据视频总时长进行均匀估算,而非全部置 0。

良好实践

  • 引入了滑动窗口匹配算法,相比单纯的平均距离,能更准确地定位重复的时间片段。
  • 使用了中位数距离代替平均距离,对异常值更具鲁棒性。
  • 融合了 pHash 和颜色直方图两种特征,提高了查重的准确率。
  • 添加了详细的类型注解和函数文档字符串。

🤖 由 AI 代码审查机器人自动生成 | 2026-09-03 15:12:10 | 模型:

### 【阻塞级判定】 - 是否存在阻塞级问题:是 - 阻塞级问题数量:3 个 ### 📊 审查概览 - 整体评价:需修改 - 建议级问题数量:4 个 ### 🔴 阻塞级问题(必须修复) 1. **[apps/worker/video_processing/dedup.py: 130-160] 严重的性能回归:全帧扫描** - 问题类型:性能问题 - 问题描述:`detect_keyframe_timestamps` 函数中使用了 `while True: cap.read()` 逐帧遍历视频,并对每一帧进行 `resize`、`cvtColor` 和 `diff` 计算。对于长视频(如1小时视频,约21.6万帧),这将导致极高的CPU消耗和处理时间,极易导致Worker超时或OOM。旧代码采用固定间隔采样,新代码改为全帧扫描未做降采样处理。 - 修改建议:在循环中增加步长,例如每5帧或每0.5秒处理一次,避免处理每一帧。 2. **[apps/worker/video_processing/dedup.py: 130-160] 逻辑错误:遗漏视频首帧** - 问题类型:逻辑bug - 问题描述:在 `detect_keyframe_timestamps` 中,`prev_gray` 初始化为 `None`,只有在 `prev_gray is not None` 时才计算差异并添加候选关键帧。这意味着视频的第一帧(timestamp=0)永远不会被添加到 `candidates` 中。如果视频开头就是重要场景,该场景会被遗漏,除非后续通过 `min_frames` 均匀补充逻辑强行加入(但这仅在总数不足时触发)。 - 修改建议:在循环开始前,显式将第一帧(timestamp=0)加入 `candidates`,或者在循环内部针对第一帧做特殊处理。 3. **[apps/worker/video_processing/dedup.py: 360] 逻辑错误:重复片段平均距离计算偏差** - 问题类型:逻辑bug - 问题描述:在 `find_duplicate_segments` 函数计算 `avg_dist` 时,是对 `start` 到 `end` 范围内的**所有**帧(包括不匹配的 gap 帧)的汉明距离求平均。由于不匹配的帧距离通常很大(> threshold),这会显著拉高 `avg_distance`,导致返回的重复片段相似度指标失真,无法反映真正匹配部分的相似度。 - 修改建议:计算 `avg_dist` 时应只统计 `is_match` 为 `True` 的帧的距离,或者确保分母只统计匹配帧数。 ### 💡 改进建议(不阻塞合并) 1. **[apps/worker/video_processing/dedup.py: 620-621] 魔法数值未使用常量** - 具体内容:在 `check_duplicate`、`check_batch_duplicate` 和 `compute_duplicate_rate` 中计算 `combined_score` 时,直接使用了 `0.7` 和 `0.3`,而文件顶部已定义了 `PHASH_WEIGHT` 和 `HISTOGRAM_WEIGHT` 常量。应统一使用常量以保持一致性。 2. **[apps/worker/video_processing/dedup.py: 593-640] 代码重复** - 具体内容:`check_duplicate` 和 `check_batch_duplicate` 方法中存在大量重复的相似度计算逻辑(计算 min_distances、match_ratio、median_distance、hist_similarity、combined_score)。建议提取一个私有方法 `_calculate_combined_similarity` 来复用逻辑。 3. **[apps/worker/video_processing/dedup.py: 138] 性能优化建议** - 具体内容:在 `detect_keyframe_timestamps` 的 `while` 循环中,使用 `cap.get(cv2.CAP_PROP_POS_MSEC)` 获取时间戳。频繁调用 `cap.get` 在某些后端(如FFmpeg)上开销较大。建议通过帧索引计数器手动计算时间戳:`current_timestamp = frame_idx / fps`。 4. **[apps/worker/video_processing/dedup.py: 630-640] 兼容性数据处理** - 具体内容:当 `chunk_data` 为空(旧数据)时,构造的 `existing_chunk_objects` 中 `start_time_ms` 和 `end_time_ms` 均为 0。这会导致 `find_duplicate_segments` 返回的重复片段中目标时间戳为 0,信息不准确。建议在无法获取时间戳时,根据视频总时长进行均匀估算,而非全部置 0。 ### ✅ 良好实践 - 引入了滑动窗口匹配算法,相比单纯的平均距离,能更准确地定位重复的时间片段。 - 使用了中位数距离代替平均距离,对异常值更具鲁棒性。 - 融合了 pHash 和颜色直方图两种特征,提高了查重的准确率。 - 添加了详细的类型注解和函数文档字符串。 --- <sub>🤖 由 AI 代码审查机器人自动生成 | 2026-09-03 15:12:10 | 模型: </sub> <!-- AI_CODE_REVIEW_AUTO_COMMENT -->
xiaoxia merged commit 1ff81dcd0a into develop 2026-09-03 23:16:51 +08:00

🗑️ 预览环境已清理

PR #1673 已关闭或合并,对应的预览环境已被清理。

如有需要,可以重新打开 PR 来重新生成预览环境。

🗑️ **预览环境已清理** PR #1673 已关闭或合并,对应的预览环境已被清理。 > 如有需要,可以重新打开 PR 来重新生成预览环境。
Sign in to join this conversation.