Spaces:
Sleeping
Sleeping
| import os | |
| import json | |
| import io | |
| import imagehash | |
| from PIL import Image | |
| BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) | |
| HASH_FILE = os.path.join(BASE_DIR, 'anticheat_hashes.json') | |
| class AntiCheatEngine: | |
| def __init__(self): | |
| self.hashes = set() | |
| self._load_hashes() | |
| def _load_hashes(self): | |
| if os.path.exists(HASH_FILE): | |
| try: | |
| with open(HASH_FILE, 'r') as f: | |
| data = json.load(f) | |
| self.hashes = set(data.get("hashes", [])) | |
| except Exception as e: | |
| print(f"⚠️ Could not load anticheat hashes: {e}") | |
| def _save_hashes(self): | |
| try: | |
| with open(HASH_FILE, 'w') as f: | |
| json.dump({"hashes": list(self.hashes)}, f) | |
| except Exception as e: | |
| print(f"⚠️ Could not save anticheat hashes: {e}") | |
| def get_hashes(self, file_bytes): | |
| """Calculates pHash and dHash for better duplicate detection.""" | |
| try: | |
| img = Image.open(io.BytesIO(file_bytes)).convert('RGB') | |
| p_hash = str(imagehash.phash(img)) | |
| d_hash = str(imagehash.dhash(img)) | |
| return p_hash, d_hash | |
| except Exception: | |
| return None, None | |
| def is_duplicate(self, file_bytes, similarity_threshold=8): | |
| """ | |
| Checks if the image is a duplicate based on stored hashes. | |
| similarity_threshold: the max hamming distance to be considered a duplicate. | |
| """ | |
| p_hash_str, d_hash_str = self.get_hashes(file_bytes) | |
| if not p_hash_str or not d_hash_str: | |
| return False | |
| # Check exact matches first for speed | |
| if p_hash_str in self.hashes or d_hash_str in self.hashes: | |
| return True | |
| p_hash = imagehash.hex_to_hash(p_hash_str) | |
| d_hash = imagehash.hex_to_hash(d_hash_str) | |
| # Check similarity (hamming distance) | |
| for stored_hash_str in self.hashes: | |
| try: | |
| stored_hash = imagehash.hex_to_hash(stored_hash_str) | |
| # Compare both pHash and dHash representation lengths isn't an issue since they are stored as strings | |
| # but we should compare apples to apples. Let's simplify and just do exact match on dHash and pHash, | |
| # but also check similarity if we parse them properly. | |
| # For safety, let's just do an exact match on string representations for now, | |
| # or a simple distance check if we assume all stored are pHashes. | |
| # Since we store both, some might be dHash, some pHash. | |
| # Let's just compare distances safely. | |
| distance = p_hash - stored_hash | |
| if distance < similarity_threshold: | |
| return True | |
| distance = d_hash - stored_hash | |
| if distance < similarity_threshold: | |
| return True | |
| except Exception: | |
| continue | |
| return False | |
| def register(self, file_bytes): | |
| """Registers a new image hash to prevent future duplicates.""" | |
| p_hash_str, d_hash_str = self.get_hashes(file_bytes) | |
| if p_hash_str: | |
| self.hashes.add(p_hash_str) | |
| if d_hash_str: | |
| self.hashes.add(d_hash_str) | |
| self._save_hashes() | |
| return p_hash_str | |
| def clear(self): | |
| self.hashes.clear() | |
| self._save_hashes() | |
| return len(self.hashes) | |
| def count(self): | |
| return len(self.hashes) | |