Spaces:
Running
Running
| from typing import IO | |
| import io | |
| import numpy as np | |
| from PIL import Image | |
| from fastapi import Depends, HTTPException, status | |
| from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer | |
| import torch | |
| from torchvision import transforms | |
| from .preprocessor import preprocessor | |
| from .inferencer import interferencer | |
| from .model_loader import models | |
| from config import Config | |
| security = HTTPBearer() | |
| async def verify_token(credentials: HTTPAuthorizationCredentials = Depends(security)): | |
| token = credentials.credentials | |
| expected_token = Config.SECRET_TOKEN | |
| if token != expected_token: | |
| raise HTTPException( | |
| status_code=status.HTTP_403_FORBIDDEN, | |
| detail="Invalid or expired token", | |
| ) | |
| return token | |
| class ClassificationController: | |
| """ | |
| Controller to handle the image classification logic. | |
| """ | |
| def classify_image(self, image_file: IO) -> dict: | |
| """ | |
| Orchestrates the classification of a single image file. | |
| Args: | |
| image_file (IO): The image file to classify. | |
| Returns: | |
| dict: The classification result. | |
| """ | |
| try: | |
| # Step 1: Preprocess the image | |
| image_tensor = preprocessor.process(image_file) | |
| # Step 2: Perform inference | |
| result = interferencer.predict(image_tensor) | |
| return result | |
| except ValueError as e: | |
| # Handle specific errors like invalid images | |
| return {"error": str(e)} | |
| except Exception as e: | |
| # Handle unexpected errors | |
| print(f"An unexpected error occurred: {e}") | |
| return {"error": "An internal error occurred during classification."} | |
| # Create a single instance of the controller | |
| controller = ClassificationController() | |
| class documentForger: | |
| """ | |
| Document forgery detector that uses the ELA-trained EfficientNet model | |
| when available (models.doc_model). Returns a dict with verdict and confidence. | |
| """ | |
| def is_forged(self, document_file: IO) -> dict: | |
| # Ensure a document model is loaded | |
| if not hasattr(models, 'doc_model') or models.doc_model is None: | |
| _downloadmodel = Config.DOCUMENT_FORGERY_MODEL_PATH | |
| return {"detail": "Document forgery model not available."} | |
| # Read file bytes | |
| try: | |
| data = document_file.read() | |
| img = Image.open(io.BytesIO(data)).convert('RGB') | |
| except Exception as e: | |
| return {"detail": f"Could not open document image: {e}"} | |
| # Compute ELA map (same approach as the notebook) | |
| try: | |
| buf = io.BytesIO() | |
| img.save(buf, format='JPEG', quality=90) | |
| buf.seek(0) | |
| recompressed = Image.open(buf).convert('RGB') | |
| ela_arr = np.abs(np.array(img, dtype=np.float32) - np.array(recompressed, dtype=np.float32)) | |
| p99 = np.percentile(ela_arr, 99) | |
| if p99 > 0: | |
| ela_arr = np.clip(ela_arr * (255.0 / p99), 0, 255).astype(np.uint8) | |
| else: | |
| ela_arr = ela_arr.astype(np.uint8) | |
| ela_pil = Image.fromarray(ela_arr, mode='RGB') | |
| except Exception as e: | |
| return {"detail": f"Failed to compute ELA: {e}"} | |
| # Transform and run through model | |
| transform = transforms.Compose([ | |
| transforms.Resize((224, 224)), | |
| transforms.ToTensor(), | |
| transforms.Normalize(mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5]), | |
| ]) | |
| tensor = transform(ela_pil).unsqueeze(0).to(models.device) | |
| with torch.no_grad(): | |
| logits = models.doc_model(tensor) | |
| probs = torch.softmax(logits, dim=1)[0, 1].item() | |
| # Interpret confidence using configurable thresholds (values in 0..1) | |
| low = getattr(Config, 'DOCUMENT_FORGERY_POSSIBLE_LOW', 0.40) | |
| high = getattr(Config, 'DOCUMENT_FORGERY_FORGED_LOW', 0.55) | |
| if probs < low: | |
| verdict = 'LIKELY AUTHENTIC' | |
| elif probs < high: | |
| verdict = 'POSSIBLY FORGED' | |
| else: | |
| verdict = 'LIKELY FORGED' | |
| return { | |
| "verdict": verdict, | |
| "confidence": float(probs), | |
| "confidence_pct": round(float(probs) * 100, 2), | |
| } | |
| # Create a single instance of the document forger | |
| document_forger = documentForger() | |