Spaces:
Sleeping
Sleeping
File size: 5,193 Bytes
7957cc3 2ad8303 b005330 2ad8303 b005330 2ad8303 7957cc3 b005330 2ad8303 b005330 2ad8303 b005330 2ad8303 7957cc3 2ad8303 7957cc3 2ad8303 7957cc3 2ad8303 7957cc3 2ad8303 b005330 2ad8303 b005330 2ad8303 b005330 |
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 |
import io
from typing import List, Tuple, Dict, Any
from PIL import Image
import numpy as np
import torch
import gradio as gr
# Face detector
from facenet_pytorch import MTCNN
# HF image classifier
from transformers import AutoImageProcessor, AutoModelForImageClassification
# ========= Config =========
# You can change the model below to another public model on Hugging Face
# Example: prithivMLmods/Deep-Fake-Detector-v2-Model (binary: Deepfake vs Realism)
MODEL_ID = "prithivMLmods/Deep-Fake-Detector-v2-Model"
DEVICE = "cpu" # Use "cuda" if GPU is available
MAX_SIDE = 640 # Resize to keep the longest side β€ 640px for efficiency
# =========================
# ---- Utilities ----
def resize_keep_ratio(img: Image.Image, max_side: int = MAX_SIDE) -> Image.Image:
"""Resize the image while keeping aspect ratio and limit max side length."""
w, h = img.size
m = max(w, h)
if m <= max_side:
return img
scale = max_side / float(m)
return img.resize((int(w * scale), int(h * scale)), Image.LANCZOS)
def canonical_label(label: str) -> str:
"""Map model-specific labels to canonical 'fake' or 'real' categories."""
l = label.lower()
if any(k in l for k in ["fake", "ai", "synthetic", "deepfake"]):
return "fake"
if any(k in l for k in ["real", "authentic", "genuine"]):
return "real"
# Default fallback if label doesn't match known keywords
return label
def rank_probs(id2label: Dict[int, str], probs: List[float]) -> List[Tuple[str, float]]:
"""Return sorted list of (label, probability) pairs."""
pairs = [(id2label[i], float(probs[i])) for i in range(len(probs))]
return sorted(pairs, key=lambda x: x[1], reverse=True)
# ---- Load models (once) ----
mtcnn = MTCNN(keep_all=True, device=DEVICE)
processor = AutoImageProcessor.from_pretrained(MODEL_ID)
clf = AutoModelForImageClassification.from_pretrained(MODEL_ID).to(DEVICE)
id2label = clf.config.id2label
# ---- Core inference ----
@torch.no_grad()
def classify_pil(img: Image.Image) -> Dict[str, Any]:
"""Run classification on a single PIL image and return ranked probabilities."""
inputs = processor(images=img, return_tensors="pt")
inputs = {k: v.to(DEVICE) for k, v in inputs.items()}
logits = clf(**inputs).logits
probs = torch.softmax(logits, dim=-1).squeeze().tolist()
ranked = rank_probs(id2label, probs)
# Extract approximate fake / real probabilities based on label keywords
fake_p, real_p = None, None
for lbl, p in ranked:
cat = canonical_label(lbl)
if cat == "fake" and fake_p is None:
fake_p = p
if cat == "real" and real_p is None:
real_p = p
return {
"top": ranked[:3],
"fake_prob": fake_p,
"real_prob": real_p
}
def analyze(img: Image.Image) -> Dict[str, Any]:
"""Main analysis pipeline: detect faces, classify each face or full image."""
if img is None:
return {"error": "No image uploaded."}
img = img.convert("RGB")
img = resize_keep_ratio(img, MAX_SIDE)
# 1) Face detection
boxes, _ = mtcnn.detect(img)
crops = []
if boxes is not None:
for (x1, y1, x2, y2) in boxes:
x1 = max(0, int(x1)); y1 = max(0, int(y1))
x2 = min(img.width, int(x2)); y2 = min(img.height, int(y2))
if x2 > x1 and y2 > y1:
crops.append(img.crop((x1, y1, x2, y2)))
results = []
if crops:
# 2) Classify each detected face
for idx, face in enumerate(crops, 1):
r = classify_pil(face)
results.append({"face": idx, **r})
else:
# 3) If no face is detected, classify the whole image
r = classify_pil(img)
results.append({"face": None, **r})
# Aggregate: use median of fake probabilities across all faces
fake_scores = []
for r in results:
if r.get("fake_prob") is not None:
fake_scores.append(r["fake_prob"])
else:
# Fallback: use top-1 label keyword
top1 = r["top"][0][0]
fake_scores.append(1.0 if canonical_label(top1) == "fake" else 0.0)
if fake_scores:
overall_fake = float(np.median(fake_scores))
else:
overall_fake = 0.5
label = "Likely AI/Deepfake" if overall_fake >= 0.6 else ("Uncertain" if overall_fake >= 0.4 else "Likely Real")
return {
"label": label,
"overall_fake_probability": round(overall_fake, 3),
"faces_detected": len(crops),
"per_face_results": results
}
# ---- Gradio UI ----
with gr.Blocks() as demo:
gr.Markdown(
"""
# π΅οΈ FakeSpotter β Image Deepfake Detector (CPU)
Upload an image. If a face is detected, each face is analyzed; otherwise, the whole image is classified.
**No EXIF is used.** Model can be swapped by editing `MODEL_ID` in the code.
> Classroom demo β not a forensic tool.
"""
)
with gr.Row():
inp = gr.Image(type="pil", label="Upload image")
out = gr.JSON(label="Results")
gr.Button("Analyze").click(analyze, inputs=inp, outputs=out)
if __name__ == "__main__":
demo.launch()
|