| from typing import IO |
| import io |
| import numpy as np |
| from PIL import Image |
| import torch |
| from torchvision import transforms |
|
|
| from .preprocessor import preprocessor |
| from .inferencer import interferencer |
| from .model_loader import models |
| from config import Config |
|
|
|
|
| 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: |
| |
| image_tensor = preprocessor.process(image_file) |
|
|
| |
| result = interferencer.predict(image_tensor) |
|
|
| return result |
| except ValueError as e: |
| |
| return {"error": str(e)} |
| except Exception as e: |
| |
| print(f"An unexpected error occurred: {e}") |
| return {"error": "An internal error occurred during classification."} |
|
|
| |
| 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: |
| |
| if not hasattr(models, 'doc_model') or models.doc_model is None: |
| return {"error": "Document forgery model not available."} |
|
|
| |
| try: |
| data = document_file.read() |
| img = Image.open(io.BytesIO(data)).convert('RGB') |
| except Exception as e: |
| return {"error": f"Could not open document image: {e}"} |
|
|
| |
| 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 {"error": f"Failed to compute ELA: {e}"} |
|
|
| |
| 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() |
|
|
| |
| 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), |
| } |
|
|
| |
| document_forger = documentForger() |
|
|