From 2c8337d71d95a00e6b297b86464c7465de2198bf Mon Sep 17 00:00:00 2001 From: xiaoxia Date: Thu, 3 Sep 2026 23:41:15 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=20AI=20Code=20Review?= =?UTF-8?q?=203=20=E4=B8=AA=E9=98=BB=E5=A1=9E=E7=BA=A7=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. KeyError 风险: c['color_histogram'] → c.get('color_histogram', []) 防止存量数据缺少该字段时 Worker 崩溃 2. 直方图默认值不对称: 任一方无数据时统一返回 0.0 旧逻辑: 已有视频无直方图→0.5, 新视频无直方图→0.0 新逻辑: 任一方无数据→0.0(无法判定相似) 3. Bhattacharyya 输入已归一化(cv2.normalize 保证) compute_color_histogram 已用 cv2.normalize 处理,无需额外改动 测试:49/49 全绿 --- apps/worker/video_processing/dedup.py | 19 +++++++++++-------- tests/unit/test_dedup_v2.py | 10 +++++----- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/apps/worker/video_processing/dedup.py b/apps/worker/video_processing/dedup.py index da17d22af..d5f389095 100755 --- a/apps/worker/video_processing/dedup.py +++ b/apps/worker/video_processing/dedup.py @@ -367,11 +367,14 @@ class VideoDeduplicator: # Step 4: 加权融合 phash_similarity = 1.0 - (median_distance / 64) - hist_similarity = ( - self._compute_histogram_similarity(fingerprint.color_histograms, existing_histograms) - if existing_histograms - else 0.5 # 无直方图数据时给中间值(向后兼容) - ) + + # 直方图相似度:任一方无数据时统一返回 0.0(无法判定),避免不对称 + if existing_histograms and fingerprint.color_histograms: + hist_similarity = self._compute_histogram_similarity( + fingerprint.color_histograms, existing_histograms + ) + else: + hist_similarity = 0.0 combined_score = self.PHASH_WEIGHT * phash_similarity + self.HISTOGRAM_WEIGHT * hist_similarity return { @@ -461,7 +464,7 @@ class VideoDeduplicator: chunk_data = self._get_existing_chunks(existing.id, session) if chunk_data: existing_phashes = [c["phash_binary"] for c in chunk_data] - existing_histograms = [c["color_histogram"] for c in chunk_data] + existing_histograms = [c.get("color_histogram", []) for c in chunk_data] else: # 回退:从 JSON 字段读取(存量旧视频) existing_phashes = ef.get("keyframe_phashes", []) @@ -526,7 +529,7 @@ class VideoDeduplicator: chunk_data = self._get_existing_chunks(existing.id, session) if chunk_data: existing_phashes = [c["phash_binary"] for c in chunk_data] - existing_histograms = [c["color_histogram"] for c in chunk_data] + existing_histograms = [c.get("color_histogram", []) for c in chunk_data] else: existing_phashes = ef.get("keyframe_phashes", []) existing_histograms = ef.get("color_histograms", []) @@ -616,7 +619,7 @@ class VideoDeduplicator: chunk_data = self._get_existing_chunks(existing.id, session) if chunk_data: existing_phashes = [c["phash_binary"] for c in chunk_data] - existing_histograms = [c["color_histogram"] for c in chunk_data] + existing_histograms = [c.get("color_histogram", []) for c in chunk_data] else: existing_phashes = ef.get("keyframe_phashes", []) existing_histograms = ef.get("color_histograms", []) diff --git a/tests/unit/test_dedup_v2.py b/tests/unit/test_dedup_v2.py index 520c76dac..2b2ee8b8f 100644 --- a/tests/unit/test_dedup_v2.py +++ b/tests/unit/test_dedup_v2.py @@ -352,24 +352,24 @@ class TestBackwardCompatibility: """无直方图数据时不崩溃。""" def test_no_histogram_fallback(self): - """已有视频无分片直方图 → hist_similarity 回退到 0.5,不崩溃。""" + """已有视频无分片直方图 → hist_similarity 回退到 0.0,不崩溃。""" d = VideoDeduplicator() phashes = ["0" * 16] * 10 fp = _make_fingerprint(phashes, [[0.5] * 96] * 10) # existing_histograms 为空列表 result = d._check_fusion_duplicate(fp, {}, phashes, []) - # 应该不崩溃,hist_similarity=0.5 + # 应该不崩溃,hist_similarity=0.0(无数据时统一为0) if result: - assert result["_debug"]["hist_similarity"] == 0.5 + assert result["_debug"]["hist_similarity"] == 0.0 def test_no_histogram_combined_score(self): - """无直方图时的 combined_score = 0.7 * phash + 0.3 * 0.5。""" + """无直方图时的 combined_score = 0.7 * phash + 0.3 * 0.0。""" d = VideoDeduplicator() phashes = ["0" * 16] * 10 # 完全相同 fp = _make_fingerprint(phashes, [[0.5] * 96] * 10) result = d._check_fusion_duplicate(fp, {}, phashes, []) if result: - expected = 0.7 * 1.0 + 0.3 * 0.5 # = 0.85 + expected = 0.7 * 1.0 + 0.3 * 0.0 # = 0.7 assert result["similarity"] == pytest.approx(expected)