Spaces:
Sleeping
Sleeping
File size: 4,418 Bytes
1970a5b | 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 | """
DeepfakeDetector β orchestrates the full video analysis pipeline
"""
import os, uuid, time
import numpy as np
import torch
import torchvision.transforms as T
from pathlib import Path
from typing import Optional
from model import HybridDeepfakeDetector
from face_extractor import FaceExtractor
from gradcam import GradCAM
# ββ Image pre-processing βββββββββββββββββββββββββββββββββββββββββββββ
MEAN = [0.485, 0.456, 0.406]
STD = [0.229, 0.224, 0.225]
transform = T.Compose([
T.ToTensor(),
T.Normalize(mean=MEAN, std=STD),
])
class DeepfakeDetector:
"""
End-to-end video deepfake detector.
Steps:
1. Sample frames from video
2. Crop face regions
3. Run hybrid model inference
4. Generate Grad-CAM heatmaps
5. Aggregate per-frame scores into video-level verdict
"""
def __init__(
self,
model_path: Optional[str] = None,
device: Optional[str] = None,
max_frames: int = 32,
threshold: float = 0.5,
):
self.device = device or ("cuda" if torch.cuda.is_available() else "cpu")
self.threshold = threshold
self.max_frames = max_frames
# Load model
self.model = HybridDeepfakeDetector(pretrained=(model_path is None))
if model_path and Path(model_path).exists():
state = torch.load(model_path, map_location=self.device)
self.model.load_state_dict(state)
print(f"[Detector] Loaded weights from {model_path}")
else:
print("[Detector] Using ImageNet pretrained weights (demo mode). "
"Train on FF++ for research-quality results.")
self.model.to(self.device).eval()
self.extractor = FaceExtractor()
self.gradcam = GradCAM(self.model)
# ββ Single frame inference βββββββββββββββββββββββββββββββββββββββ
def _infer_frame(self, face_rgb: np.ndarray) -> float:
"""Returns fake probability for one face crop."""
tensor = transform(face_rgb).unsqueeze(0).to(self.device)
with torch.no_grad():
prob = self.model.predict_proba(tensor).item()
return float(prob)
# ββ Main analysis ββββββββββββββββββββββββββββββββββββββββββββββββ
def analyze(self, video_path: str, session_id: Optional[str] = None) -> dict:
t0 = time.time()
session_id = session_id or str(uuid.uuid4())
results_dir = Path("results") / session_id
results_dir.mkdir(parents=True, exist_ok=True)
# 1. Extract faces
faces = self.extractor.extract(video_path, max_frames=self.max_frames)
if not faces:
return {
"session_id": session_id,
"verdict": "UNKNOWN",
"confidence": 0.0,
"error": "No faces detected in video.",
"frames": [],
}
# 2. Per-frame inference + Grad-CAM
frame_results = []
scores = []
for i, item in enumerate(faces):
face_rgb = item["face"]
fidx = item["frame_idx"]
prob = self._infer_frame(face_rgb)
scores.append(prob)
# Generate and save Grad-CAM heatmap
cam_path = str(results_dir / f"frame_{i:04d}_cam.jpg")
self.gradcam.generate(face_rgb, cam_path)
frame_results.append({
"frame_idx": fidx,
"fake_prob": round(prob, 4),
"verdict": "FAKE" if prob >= self.threshold else "REAL",
"cam_path": f"/results/{session_id}/frame_{i:04d}_cam.jpg",
})
# 3. Video-level score
video_score = float(np.mean(scores))
verdict = "FAKE" if video_score >= self.threshold else "REAL"
elapsed = round(time.time() - t0, 2)
return {
"session_id": session_id,
"verdict": verdict,
"confidence": round(video_score * 100, 2),
"fake_prob": round(video_score, 4),
"frames_analyzed": len(faces),
"elapsed_sec": elapsed,
"frame_scores": frame_results,
}
|