Pujan-Dev's picture
Add document forgery detection feature and refactor model loading
c8f4e3f
raw
history blame
3.8 kB
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:
# 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:
return {"error": "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 {"error": 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 {"error": 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()