From a97e3e72e9ee0840f6894ebd7d0ca5ae44b1f8d6 Mon Sep 17 00:00:00 2001 From: CI Bot Date: Fri, 26 Jun 2026 22:46:21 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=20dedup.py=20?= =?UTF-8?q?=E8=AF=AD=E6=B3=95=E9=94=99=E8=AF=AF=E5=92=8C=20pHash=20DCT=20?= =?UTF-8?q?=E5=AE=9E=E7=8E=B0=EF=BC=8C=E4=BF=AE=E5=A4=8D=20chunked=5Fuploa?= =?UTF-8?q?d.py=20=E7=BC=A9=E8=BF=9B=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/api/app/api/routes/chunked_upload.py | 2 +- apps/worker/video_processing/dedup.py | 20 ++++++++++++++------ 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/apps/api/app/api/routes/chunked_upload.py b/apps/api/app/api/routes/chunked_upload.py index 45664d786..d2c1e49cf 100644 --- a/apps/api/app/api/routes/chunked_upload.py +++ b/apps/api/app/api/routes/chunked_upload.py @@ -173,7 +173,7 @@ def _cleanup_expired_uploads() -> int: expires_at = expires_at.replace(tzinfo=timezone.utc) # Only cleanup uploads that are not actively being uploaded - if expires_at < now and meta.get("status") != "uploading": + if expires_at < now and meta.get("status") != "uploading": upload_id = meta["upload_id"] chunk_dir = _get_chunk_dir(upload_id) if chunk_dir.exists(): diff --git a/apps/worker/video_processing/dedup.py b/apps/worker/video_processing/dedup.py index 492ef0995..dbc69308a 100644 --- a/apps/worker/video_processing/dedup.py +++ b/apps/worker/video_processing/dedup.py @@ -27,12 +27,20 @@ if SessionLocal is None: def compute_phash(image: np.ndarray, hash_size: int = 8) -> str: - """Compute perceptual hash of an image.""" - image = cv2.resize(image, (hash_size * 4, hash_size * 4)) - gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) - avg = gray.mean() - diff = (gray > avg).astype(int) - hash_str = """.join(str(b) for row in diff for b in row) + """Compute perceptual hash of an image using DCT.""" + # Resize to 32x32 for DCT + resized = cv2.resize(image, (hash_size * 4, hash_size * 4)) + gray = cv2.cvtColor(resized, cv2.COLOR_BGR2GRAY).astype(np.float32) + # Apply 2D DCT + dct = cv2.dct(gray) + # Take top-left 8x8 low-frequency components + dct_low = dct[:hash_size, :hash_size] + # Compute median (excluding DC component at [0,0]) + dct_low[0, 0] = 0 + median = np.median(dct_low) + # Generate hash based on comparison with median + diff = (dct_low > median).astype(int) + hash_str = "".join(str(b) for row in diff for b in row) return hex(int(hash_str, 2))[2:]