| """ |
| Duplicate detection forensics provider. |
| |
| Delegates pHash / dHash / SHA-256 / Hamming distance to cores.vision — |
| no duplicated hashing logic. Maintains an in-memory registry of seen |
| hashes for duplicate detection across jobs. |
| """ |
|
|
| from __future__ import annotations |
|
|
| import numpy as np |
|
|
| from config.settings import Settings, settings as _default_settings |
| from cores.vision import phash, dhash, sha256_bytes, hamming_distance |
| from pipeline.feature_extraction import PipelineOutput |
| from providers.base import BaseProvider, ProviderCapability |
|
|
|
|
| class DuplicateDetectorProvider(BaseProvider): |
| name = "duplicate_detector" |
| capability = ProviderCapability.FORENSICS |
|
|
| DUPLICATE_THRESHOLD = 5 |
|
|
| def __init__(self, settings: Settings | None = None) -> None: |
| super().__init__(settings=settings or _default_settings) |
| |
| self._seen: dict[str, str] = {} |
|
|
| def is_available(self) -> bool: |
| return True |
|
|
| def _run(self, pipeline_output: PipelineOutput) -> tuple[dict, dict]: |
| img: np.ndarray = pipeline_output.image |
| p = phash(img) |
| d = dhash(img) |
| sha = sha256_bytes(pipeline_output.original_bytes) if pipeline_output.original_bytes else None |
|
|
| |
| duplicate_of = None |
| is_duplicate = False |
| similarity = 1.0 |
|
|
| for stored_hash, label in self._seen.items(): |
| dist = hamming_distance(p, stored_hash) |
| similarity = 1.0 - (dist / 64.0) |
| if dist <= self.DUPLICATE_THRESHOLD: |
| duplicate_of = label |
| is_duplicate = True |
| break |
|
|
| |
| if sha: |
| self._seen[p] = sha[:12] |
|
|
| raw = { |
| "phash": p, |
| "dhash": d, |
| "sha256": sha, |
| "is_duplicate": is_duplicate, |
| "duplicate_of": duplicate_of, |
| "similarity_score": round(similarity, 4), |
| "registered_hashes": len(self._seen), |
| } |
| normalized = { |
| "integrity_score": None, |
| "is_duplicate": is_duplicate, |
| "duplicate_of": duplicate_of, |
| "similarity_score": round(similarity, 4), |
| "manipulation_indicators": [], |
| "details": { |
| "phash": p, |
| "dhash": d, |
| "sha256": sha, |
| "registered_hashes": len(self._seen), |
| }, |
| } |
| return raw, normalized |
|
|