ThinothW commited on
Commit
c84070a
·
verified ·
1 Parent(s): 5b75694

Create inference.py

Browse files
Files changed (1) hide show
  1. inference.py +120 -0
inference.py ADDED
@@ -0,0 +1,120 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import cv2
2
+ import numpy as np
3
+ from PIL import Image
4
+ import torch
5
+ import torch.nn as nn
6
+ from torchvision import models, transforms
7
+ from retinaface import RetinaFace
8
+ from pathlib import Path
9
+ from typing import Optional
10
+
11
+ # --- Configuration ---
12
+ CHECKPOINT_PATH = Path("pytorch_model.bin") # Updated to match your new HF filename
13
+ _IN_FEATURES = 1408
14
+ _DROPOUT = 0.3
15
+ _NUM_CLASSES = 2
16
+ _INPUT_SIZE = 260
17
+ _CONFIDENCE_THRESHOLD = 0.90
18
+ _MIN_FACE_PX = 50
19
+ _PADDING = 20
20
+
21
+ # --- Transform ---
22
+ _transform = transforms.Compose([
23
+ transforms.Resize((_INPUT_SIZE, _INPUT_SIZE), interpolation=transforms.InterpolationMode.BICUBIC),
24
+ transforms.ToTensor(),
25
+ transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
26
+ ])
27
+
28
+ # --- 1. Load Architecture ---
29
+ def load_model() -> tuple[nn.Module, torch.device]:
30
+ # Universal Hardware Routing
31
+ if torch.cuda.is_available():
32
+ device = torch.device("cuda")
33
+ elif torch.backends.mps.is_available():
34
+ device = torch.device("mps")
35
+ else:
36
+ device = torch.device("cpu")
37
+
38
+ net = models.efficientnet_b2(weights=None)
39
+ net.classifier = nn.Sequential(
40
+ nn.Dropout(_DROPOUT),
41
+ nn.Linear(_IN_FEATURES, _NUM_CLASSES),
42
+ )
43
+
44
+ checkpoint = torch.load(CHECKPOINT_PATH, map_location=device, weights_only=False)
45
+ net.load_state_dict(checkpoint["model_state_dict"])
46
+
47
+ net.to(device)
48
+ net.eval()
49
+
50
+ return net, device
51
+
52
+ MODEL, DEVICE = load_model()
53
+
54
+ # --- 2. Extract & Preprocess ---
55
+ def detect_and_crop_face(image_path: str) -> Optional[torch.Tensor]:
56
+ image_bgr = cv2.imread(image_path)
57
+ if image_bgr is None:
58
+ raise ValueError(f"Could not load image at {image_path}")
59
+
60
+ detections = RetinaFace.detect_faces(image_bgr)
61
+ if not isinstance(detections, dict):
62
+ return None
63
+
64
+ best_conf = -1.0
65
+ best_box = None
66
+
67
+ for face_data in detections.values():
68
+ conf = float(face_data.get("score", 0.0))
69
+ if conf < _CONFIDENCE_THRESHOLD:
70
+ continue
71
+
72
+ x1, y1, x2, y2 = face_data["facial_area"]
73
+ w, h = x2 - x1, y2 - y1
74
+ if w < _MIN_FACE_PX or h < _MIN_FACE_PX:
75
+ continue
76
+
77
+ if conf > best_conf:
78
+ best_conf = conf
79
+ best_box = (x1, y1, x2, y2)
80
+
81
+ if best_box is None:
82
+ return None
83
+
84
+ H, W = image_bgr.shape[:2]
85
+ x1, y1, x2, y2 = best_box
86
+ x1 = max(0, x1 - _PADDING)
87
+ y1 = max(0, y1 - _PADDING)
88
+ x2 = min(W, x2 + _PADDING)
89
+ y2 = min(H, y2 + _PADDING)
90
+
91
+ crop_bgr = image_bgr[y1:y2, x1:x2]
92
+ crop_rgb = cv2.cvtColor(crop_bgr, cv2.COLOR_BGR2RGB)
93
+ pil_face = Image.fromarray(crop_rgb)
94
+
95
+ return _transform(pil_face).unsqueeze(0)
96
+
97
+ # --- 3. Execute Prediction ---
98
+ def predict_deepfake(image_path: str) -> dict:
99
+ face_tensor = detect_and_crop_face(image_path)
100
+
101
+ if face_tensor is None:
102
+ return {"error": "No face detected in the image meeting confidence thresholds."}
103
+
104
+ face_tensor = face_tensor.to(DEVICE)
105
+
106
+ with torch.no_grad():
107
+ logits = MODEL(face_tensor)
108
+ probs = torch.softmax(logits, dim=1)[0]
109
+
110
+ fake_prob = probs[0].item() * 100
111
+ real_prob = probs[1].item() * 100
112
+ predicted_idx = int(torch.argmax(probs).item())
113
+
114
+ prediction = "REAL" if predicted_idx == 1 else "FAKE"
115
+
116
+ return {
117
+ "prediction": prediction,
118
+ "fake_confidence": f"{fake_prob:.2f}%",
119
+ "real_confidence": f"{real_prob:.2f}%"
120
+ }