Spaces:
Runtime error
Runtime error
| import numpy as np | |
| import cv2 | |
| class TextureAnalyzer: | |
| def __init__(self): | |
| """ | |
| Initializes the Texture Analyzer. | |
| """ | |
| pass | |
| def _compute_lbp(self, img_gray): | |
| """ | |
| Computes a simplified Local Binary Pattern (LBP) using numpy. | |
| """ | |
| img = img_gray.astype(np.float32) | |
| out = np.zeros_like(img, dtype=np.uint8) | |
| # 8-neighbor LBP | |
| center = img[1:-1, 1:-1] | |
| out[1:-1, 1:-1] |= (img[0:-2, 0:-2] >= center).astype(np.uint8) * 1 | |
| out[1:-1, 1:-1] |= (img[0:-2, 1:-1] >= center).astype(np.uint8) * 2 | |
| out[1:-1, 1:-1] |= (img[0:-2, 2:] >= center).astype(np.uint8) * 4 | |
| out[1:-1, 1:-1] |= (img[1:-1, 2:] >= center).astype(np.uint8) * 8 | |
| out[1:-1, 1:-1] |= (img[2:, 2:] >= center).astype(np.uint8) * 16 | |
| out[1:-1, 1:-1] |= (img[2:, 1:-1] >= center).astype(np.uint8) * 32 | |
| out[1:-1, 1:-1] |= (img[2:, 0:-2] >= center).astype(np.uint8) * 64 | |
| out[1:-1, 1:-1] |= (img[1:-1, 0:-2] >= center).astype(np.uint8) * 128 | |
| return out | |
| def analyze(self, image_pil): | |
| """ | |
| Analyzes the micro-textures of the image. | |
| Generators often struggle with micro-textures, leading to over-smoothing or unnatural patterns. | |
| :param image_pil: PIL Image. | |
| :return: dict with 'score' (0 to 1) and 'confidence'. | |
| """ | |
| try: | |
| img_gray = cv2.cvtColor(np.array(image_pil), cv2.COLOR_RGB2GRAY) | |
| # Measure global blur/smoothness using Laplacian variance | |
| laplacian_var = cv2.Laplacian(img_gray, cv2.CV_64F).var() | |
| # Deepfakes (especially early ones or strong face swaps) often have very low variance (over-smoothed skin) | |
| # Or very high variance (noise artifacts). We'll map this heuristically. | |
| # Normal well-lit face: 100 - 500 | |
| smoothness_anomaly = 0.0 | |
| if laplacian_var < 50.0: # Unnaturally smooth | |
| smoothness_anomaly = min(1.0, (50.0 - laplacian_var) / 50.0) | |
| # LBP Histogram analysis | |
| lbp = self._compute_lbp(img_gray) | |
| hist, _ = np.histogram(lbp.ravel(), bins=256, range=(0, 256)) | |
| hist = hist.astype("float") | |
| hist /= (hist.sum() + 1e-7) | |
| # Calculate entropy of LBP histogram as a texture complexity measure | |
| entropy = -np.sum(hist * np.log2(hist + 1e-7)) | |
| # Normal face texture entropy is usually around 5.5 - 7.5 depending on resolution | |
| # Very low entropy means lack of texture variation (typical of AI smoothing) | |
| texture_anomaly = 0.0 | |
| if entropy < 5.0: | |
| texture_anomaly = min(1.0, (5.0 - entropy) / 2.0) | |
| elif entropy > 7.8: # Unnatural noise | |
| texture_anomaly = min(1.0, (entropy - 7.8) / 1.0) | |
| # Combine signals | |
| final_score = (smoothness_anomaly * 0.4) + (texture_anomaly * 0.6) | |
| return { | |
| "score": float(final_score), | |
| "confidence": 0.6, | |
| "anomaly_detected": final_score > 0.4, | |
| "laplacian_variance": float(laplacian_var), | |
| "lbp_entropy": float(entropy) | |
| } | |
| except Exception as e: | |
| print(f"Texture Analysis Error: {e}") | |
| return {"score": 0.0, "confidence": 0.0, "anomaly_detected": False, "error": str(e)} | |
| if __name__ == "__main__": | |
| from PIL import Image | |
| import sys | |
| if len(sys.argv) > 1: | |
| img = Image.open(sys.argv[1]).convert("RGB") | |
| analyzer = TextureAnalyzer() | |
| res = analyzer.analyze(img) | |
| print(f"Texture Result: {res}") | |