Spaces:
Sleeping
Sleeping
File size: 7,334 Bytes
a02f72f | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 | """
DeepGuard β FastAPI Backend
Real-time, stateless deepfake detection API.
Endpoints:
GET /health β Liveness check (used by extension popup)
POST /analyze β Analyze an image for AI-generation artifacts
All data is processed in RAM and dropped immediately after the response.
"""
import io
import base64
import traceback
from contextlib import asynccontextmanager
from fastapi import FastAPI, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel
from PIL import Image
import inference
import metadata as meta_module
import heatmap as heatmap_module
# ---------------------------------------------------------------------------
# Lifespan: load model once at startup
# ---------------------------------------------------------------------------
@asynccontextmanager
async def lifespan(app: FastAPI):
print("[DeepGuard] Starting up β loading ONNX model...")
try:
inference.load_model()
print("[DeepGuard] Model ready. Server is live at http://localhost:8000")
except FileNotFoundError as e:
print(f"[DeepGuard] WARNING: {e}")
print("[DeepGuard] Run 'python download_model.py' to fetch the model.")
yield
print("[DeepGuard] Shutting down.")
# ---------------------------------------------------------------------------
# App setup
# ---------------------------------------------------------------------------
app = FastAPI(
title="DeepGuard API",
description="Real-time stateless deepfake detection using ViT + ONNX Runtime",
version="1.0.0",
lifespan=lifespan,
)
# Aggressive CORS β required because Chrome extensions use a chrome-extension:// origin
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=False,
allow_methods=["*"],
allow_headers=["*"],
)
import ela as ela_module
# ---------------------------------------------------------------------------
# Request / Response schemas
# ---------------------------------------------------------------------------
class AnalyzeRequest(BaseModel):
image_data: str # data:image/...;base64,<payload> OR raw base64
class MetadataResult(BaseModel):
exif_data_present: bool
software_signature_found: str
warning: str
class ForensicsResult(BaseModel):
model_reasoning: str
metadata: MetadataResult
class AnalyzeResponse(BaseModel):
status: str
threat_level: str
confidence_score: float
heatmap_overlay_url: str
ela_overlay_url: str
forensics: ForensicsResult
# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------
def _decode_image(image_data: str) -> tuple[bytes, Image.Image]:
"""
Decode a base64 data-URI or raw base64 string into (raw_bytes, PIL Image).
"""
if image_data.startswith("data:"):
# Strip "data:image/jpeg;base64," prefix
header, b64_str = image_data.split(",", 1)
else:
b64_str = image_data
try:
raw_bytes = base64.b64decode(b64_str)
except Exception:
raise HTTPException(status_code=400, detail="Invalid base64 image data.")
try:
image = Image.open(io.BytesIO(raw_bytes)).convert("RGB")
except Exception:
raise HTTPException(status_code=400, detail="Could not decode image from base64 payload.")
return raw_bytes, image
# ---------------------------------------------------------------------------
# Endpoints
# ---------------------------------------------------------------------------
@app.get("/health")
async def health():
"""Liveness check. Returns model load status."""
try:
session = inference.get_session()
return {
"status": "ok",
"model_loaded": True,
"attention_heatmap": inference.has_attention_outputs(),
}
except RuntimeError:
return {
"status": "degraded",
"model_loaded": False,
"attention_heatmap": False,
"message": "Model not loaded. Run python download_model.py",
}
@app.post("/analyze", response_model=AnalyzeResponse)
async def analyze(request: AnalyzeRequest):
"""
Main analysis endpoint.
Accepts a base64-encoded image and returns:
- Deepfake confidence score
- Threat level classification
- Grad-CAM / attention heatmap overlay (base64 PNG)
- EXIF metadata forensics
"""
# ββ 1. Decode image ββββββββββββββββββββββββββββββββββββββββββββββββββ
try:
raw_bytes, image = _decode_image(request.image_data)
except HTTPException:
raise
except Exception as e:
raise HTTPException(status_code=400, detail=f"Image decode error: {e}")
# ββ 2. Metadata forensics (DFIR) βββββββββββββββββββββββββββββββββββββ
try:
forensic_meta = meta_module.extract_metadata(raw_bytes)
except Exception:
forensic_meta = {
"exif_data_present": False,
"software_signature_found": "None",
"warning": "Metadata extraction failed.",
}
# ββ 3. AI inference βββββββββββββββββββββββββββββββββββββββββββββββββββ
try:
confidence_score, output_dict = inference.run_inference(image)
except RuntimeError as e:
raise HTTPException(
status_code=503,
detail=f"Model not loaded: {e}. Run python download_model.py first.",
)
except Exception as e:
import traceback
traceback.print_exc()
raise HTTPException(status_code=500, detail=f"Inference error: {e}")
# ββ 4. Heatmap & ELA generation ββββββββββββββββββββββββββββββββββββββββ
try:
heatmap_url = heatmap_module.generate_heatmap(image, output_dict, confidence_score)
except Exception:
import traceback
traceback.print_exc()
# Fallback: return a 1Γ1 transparent PNG
heatmap_url = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg=="
# Run Error Level Analysis (ELA)
ela_url = ela_module.generate_ela(image)
# ββ 5. Build response βββββββββββββββββββββββββββββββββββββββββββββββββ
threat_level = inference.get_threat_level(confidence_score)
model_reasoning = inference.get_model_reasoning(
confidence_score,
forensic_meta["exif_data_present"],
forensic_meta["software_signature_found"],
)
return AnalyzeResponse(
status="success",
threat_level=threat_level,
confidence_score=round(confidence_score, 4),
heatmap_overlay_url=heatmap_url,
ela_overlay_url=ela_url,
forensics=ForensicsResult(
model_reasoning=model_reasoning,
metadata=MetadataResult(**forensic_meta),
),
)
|