DegenGamer1702 commited on
Commit
fdf3c02
·
verified ·
1 Parent(s): 758f055

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +243 -237
app.py CHANGED
@@ -1,237 +1,243 @@
1
- import os, io, tempfile, warnings
2
- import numpy as np
3
- import gradio as gr
4
-
5
- # =========================
6
- # TensorFlow / Keras
7
- # =========================
8
- import tensorflow as tf
9
- from tensorflow.keras.applications.resnet50 import preprocess_input as resnet_preprocess
10
-
11
- # GPU config for TensorFlow (enable if available)
12
- gpus = tf.config.experimental.list_physical_devices("GPU")
13
- if gpus:
14
- try:
15
- for g in gpus:
16
- tf.config.experimental.set_memory_growth(g, True)
17
- # Mixed precision can speed up on modern GPUs
18
- try:
19
- tf.keras.mixed_precision.set_global_policy("mixed_float16")
20
- print("[INFO] TF mixed precision enabled.")
21
- except Exception as e:
22
- print(f"[WARN] Could not enable mixed precision: {e}")
23
- except Exception as e:
24
- print(f"[WARN] Could not set memory growth: {e}")
25
-
26
- # =========================
27
- # Vision stack
28
- # =========================
29
- import cv2
30
- import torch
31
- from facenet_pytorch import MTCNN
32
- import dlib
33
- from imutils import face_utils
34
- from scipy.spatial import distance as dist
35
-
36
- warnings.filterwarnings("ignore")
37
-
38
- # =========================
39
- # Paths / Config
40
- # =========================
41
- VIDEO_MODEL_PATH = "models/video_model.h5"
42
- DLIB_LANDMARK_MODEL = "shape_predictor_68_face_landmarks.dat"
43
-
44
- IMG_SIZE = (224, 224)
45
- FRAME_STEP = 5 # every 5th frame
46
- NUM_MAX_FACES = 300 # cap on faces collected
47
-
48
- # Blink (EAR) features
49
- EAR_THRESHOLD = 0.25
50
- EAR_CONSEC_FRAMES = 3
51
-
52
- # Prediction threshold
53
- PRED_THRESHOLD = 0.5
54
-
55
- # =========================
56
- # Lazy-loaded state
57
- # =========================
58
- _video_model = None
59
- _mtcnn = None
60
- _dlib_detector = None
61
- _dlib_predictor = None
62
-
63
- # Torch / MTCNN device selection
64
- _torch_device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
65
- if torch.cuda.is_available():
66
- torch.backends.cudnn.benchmark = True
67
- try:
68
- torch.set_float32_matmul_precision("medium")
69
- except Exception:
70
- pass
71
- print(f"[INFO] PyTorch device: {_torch_device}")
72
-
73
- def lazy_load():
74
- global _video_model, _mtcnn, _dlib_detector, _dlib_predictor
75
-
76
- if _video_model is None:
77
- if not os.path.exists(VIDEO_MODEL_PATH):
78
- raise FileNotFoundError(f"Missing: {VIDEO_MODEL_PATH}")
79
- # Allow tf to place on GPU if available
80
- _video_model = tf.keras.models.load_model(VIDEO_MODEL_PATH)
81
- print("[INFO] Video model loaded.")
82
-
83
- if _mtcnn is None:
84
- _mtcnn = MTCNN(keep_all=True, device=_torch_device, image_size=IMG_SIZE[0])
85
- print(f"[INFO] MTCNN ready on {_torch_device}.")
86
-
87
- if _dlib_detector is None or _dlib_predictor is None:
88
- if not os.path.exists(DLIB_LANDMARK_MODEL):
89
- raise FileNotFoundError(
90
- f"Missing dlib predictor: {DLIB_LANDMARK_MODEL}. Place it beside app.py."
91
- )
92
- _dlib_detector = dlib.get_frontal_face_detector()
93
- _dlib_predictor = dlib.shape_predictor(DLIB_LANDMARK_MODEL)
94
- print("[INFO] dlib detector + predictor ready.")
95
-
96
- # =========================
97
- # VIDEO: faces + blink features
98
- # =========================
99
- def _eye_aspect_ratio(eye_pts):
100
- A = dist.euclidean(eye_pts[1], eye_pts[5])
101
- B = dist.euclidean(eye_pts[2], eye_pts[4])
102
- C = dist.euclidean(eye_pts[0], eye_pts[3])
103
- return (A + B) / (2.0 * C + 1e-9)
104
-
105
- def extract_faces_all(path):
106
- """Sample every 5th frame, keep ALL faces per frame, resize to 224x224."""
107
- cap = cv2.VideoCapture(path)
108
- total = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
109
- if total <= 0:
110
- cap.release()
111
- return None
112
-
113
- faces = []
114
- for idx in range(0, total, FRAME_STEP):
115
- if len(faces) >= NUM_MAX_FACES:
116
- break
117
- cap.set(cv2.CAP_PROP_POS_FRAMES, idx)
118
- ok, frame = cap.read()
119
- if not ok:
120
- break
121
- rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
122
- boxes, _ = _mtcnn.detect(rgb)
123
- if boxes is None:
124
- continue
125
- for (x1, y1, x2, y2) in boxes.astype(int):
126
- x1, y1 = max(0, x1), max(0, y1)
127
- x2, y2 = min(frame.shape[1], x2), min(frame.shape[0], y2)
128
- crop = frame[y1:y2, x1:x2]
129
- if crop.size > 0:
130
- faces.append(cv2.resize(crop, IMG_SIZE))
131
- if len(faces) >= NUM_MAX_FACES:
132
- break
133
- cap.release()
134
- if not faces:
135
- return None
136
- return np.stack(faces, axis=0).astype(np.uint8)
137
-
138
- def blink_features_from_crops(faces):
139
- """Compute EAR on cropped faces; denominator = #frames with valid EAR."""
140
- (lStart, lEnd) = face_utils.FACIAL_LANDMARKS_IDXS["left_eye"]
141
- (rStart, rEnd) = face_utils.FACIAL_LANDMARKS_IDXS["right_eye"]
142
-
143
- ear_values = []
144
- blink_counter = 0
145
- total_blinks = 0
146
-
147
- for face in faces:
148
- gray = cv2.cvtColor(face, cv2.COLOR_BGR2GRAY)
149
- rects = _dlib_detector(gray, 0)
150
- if len(rects) == 0:
151
- continue
152
- shape = _dlib_predictor(gray, rects[0])
153
- shape = face_utils.shape_to_np(shape)
154
-
155
- left_eye = shape[lStart:lEnd]
156
- right_eye = shape[rStart:rEnd]
157
- ear = 0.5 * (_eye_aspect_ratio(left_eye) + _eye_aspect_ratio(right_eye))
158
- ear_values.append(ear)
159
-
160
- if ear < EAR_THRESHOLD:
161
- blink_counter += 1
162
- else:
163
- if blink_counter >= EAR_CONSEC_FRAMES:
164
- total_blinks += 1
165
- blink_counter = 0
166
-
167
- n_ear = len(ear_values)
168
- if n_ear == 0:
169
- return np.array([0, 0.0, 0.0], dtype=np.float32)
170
-
171
- blink_freq = total_blinks / float(n_ear)
172
- ear_var = float(np.var(np.array(ear_values, dtype=np.float32)))
173
- return np.array([total_blinks, blink_freq, ear_var], dtype=np.float32)
174
-
175
- def predict_video_prob(video_path):
176
- faces = extract_faces_all(video_path)
177
- if faces is None or len(faces) == 0:
178
- return None, "No faces detected."
179
-
180
- blink_feats = blink_features_from_crops(faces)
181
- # Tile to per-face samples
182
- tiled = np.tile(blink_feats, (faces.shape[0], 1)).astype(np.float32)
183
- imgs = resnet_preprocess(faces.astype(np.float32))
184
-
185
- # Batch size heuristic (bigger if GPU is present)
186
- has_tf_gpu = len(tf.config.experimental.list_physical_devices("GPU")) > 0
187
- bs = 128 if has_tf_gpu else 32
188
-
189
- preds = []
190
- for i in range(0, imgs.shape[0], bs):
191
- p = _video_model.predict([imgs[i:i+bs], tiled[i:i+bs]], verbose=0)
192
- preds.append(p.reshape(-1))
193
- return float(np.mean(np.concatenate(preds, axis=0))), None
194
-
195
- def to_verdict(score):
196
- return "DEEPFAKE" if score >= PRED_THRESHOLD else "REAL"
197
-
198
- # =========================
199
- # Inference entry
200
- # =========================
201
- def run_inference(video_file):
202
- lazy_load()
203
- if video_file is None:
204
- return None, None, "Please upload a video file."
205
-
206
- video_prob, vmsg = predict_video_prob(video_file)
207
- if video_prob is None:
208
- return None, None, vmsg or "Unable to process the video."
209
-
210
- verdict = f"VIDEO ONLY: {to_verdict(video_prob)}"
211
- fmt = lambda x: None if x is None else round(float(x), 4)
212
- return fmt(video_prob), verdict, None
213
-
214
- # =========================
215
- # Gradio UI
216
- # =========================
217
- with gr.Blocks(title="Deepfake Detector Video Only (GPU-ready)") as demo:
218
- gr.Markdown(
219
- "### 🎭 Deepfake Detector — **Video Only**\n"
220
- "- **Compute**: Uses GPU automatically if available (PyTorch MTCNN, TensorFlow model; mixed precision on TF).\n"
221
- "- **Video path**: samples every 5th frame, keeps **all** faces, resizes to 224×224.\n"
222
- "- **Features**: ResNet50 preprocessing + blink EAR features on cropped faces via dlib.\n"
223
- f"- **Threshold**: {PRED_THRESHOLD} (≥ means DEEPFAKE).\n"
224
- )
225
- with gr.Row():
226
- video_in = gr.Video(label="Video")
227
- go = gr.Button("Analyze")
228
- with gr.Row():
229
- v_out = gr.Number(label="Video probability (deepfake)", precision=4)
230
- verdict_out = gr.Textbox(label="Verdict", interactive=False)
231
- msg_out = gr.Textbox(label="Message / Warnings", interactive=False)
232
-
233
- go.click(run_inference, inputs=[video_in], outputs=[v_out, verdict_out, msg_out])
234
-
235
- if __name__ == "__main__":
236
- lazy_load()
237
- demo.launch(server_name="0.0.0.0", server_port=7860)
 
 
 
 
 
 
 
1
+ import os, io, tempfile, warnings
2
+ import numpy as np
3
+ import gradio as gr
4
+ import os
5
+
6
+ # 🔧 Override Hugging Face’s wrong environment variable
7
+ os.environ["OMP_NUM_THREADS"] = "4"
8
+ os.environ["MKL_NUM_THREADS"] = "4"
9
+
10
+
11
+ # =========================
12
+ # TensorFlow / Keras
13
+ # =========================
14
+ import tensorflow as tf
15
+ from tensorflow.keras.applications.resnet50 import preprocess_input as resnet_preprocess
16
+
17
+ # GPU config for TensorFlow (enable if available)
18
+ gpus = tf.config.experimental.list_physical_devices("GPU")
19
+ if gpus:
20
+ try:
21
+ for g in gpus:
22
+ tf.config.experimental.set_memory_growth(g, True)
23
+ # Mixed precision can speed up on modern GPUs
24
+ try:
25
+ tf.keras.mixed_precision.set_global_policy("mixed_float16")
26
+ print("[INFO] TF mixed precision enabled.")
27
+ except Exception as e:
28
+ print(f"[WARN] Could not enable mixed precision: {e}")
29
+ except Exception as e:
30
+ print(f"[WARN] Could not set memory growth: {e}")
31
+
32
+ # =========================
33
+ # Vision stack
34
+ # =========================
35
+ import cv2
36
+ import torch
37
+ from facenet_pytorch import MTCNN
38
+ import dlib
39
+ from imutils import face_utils
40
+ from scipy.spatial import distance as dist
41
+
42
+ warnings.filterwarnings("ignore")
43
+
44
+ # =========================
45
+ # Paths / Config
46
+ # =========================
47
+ VIDEO_MODEL_PATH = "models/video_model.h5"
48
+ DLIB_LANDMARK_MODEL = "shape_predictor_68_face_landmarks.dat"
49
+
50
+ IMG_SIZE = (224, 224)
51
+ FRAME_STEP = 5 # every 5th frame
52
+ NUM_MAX_FACES = 300 # cap on faces collected
53
+
54
+ # Blink (EAR) features
55
+ EAR_THRESHOLD = 0.25
56
+ EAR_CONSEC_FRAMES = 3
57
+
58
+ # Prediction threshold
59
+ PRED_THRESHOLD = 0.5
60
+
61
+ # =========================
62
+ # Lazy-loaded state
63
+ # =========================
64
+ _video_model = None
65
+ _mtcnn = None
66
+ _dlib_detector = None
67
+ _dlib_predictor = None
68
+
69
+ # Torch / MTCNN device selection
70
+ _torch_device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
71
+ if torch.cuda.is_available():
72
+ torch.backends.cudnn.benchmark = True
73
+ try:
74
+ torch.set_float32_matmul_precision("medium")
75
+ except Exception:
76
+ pass
77
+ print(f"[INFO] PyTorch device: {_torch_device}")
78
+
79
+ def lazy_load():
80
+ global _video_model, _mtcnn, _dlib_detector, _dlib_predictor
81
+
82
+ if _video_model is None:
83
+ if not os.path.exists(VIDEO_MODEL_PATH):
84
+ raise FileNotFoundError(f"Missing: {VIDEO_MODEL_PATH}")
85
+ # Allow tf to place on GPU if available
86
+ _video_model = tf.keras.models.load_model(VIDEO_MODEL_PATH)
87
+ print("[INFO] Video model loaded.")
88
+
89
+ if _mtcnn is None:
90
+ _mtcnn = MTCNN(keep_all=True, device=_torch_device, image_size=IMG_SIZE[0])
91
+ print(f"[INFO] MTCNN ready on {_torch_device}.")
92
+
93
+ if _dlib_detector is None or _dlib_predictor is None:
94
+ if not os.path.exists(DLIB_LANDMARK_MODEL):
95
+ raise FileNotFoundError(
96
+ f"Missing dlib predictor: {DLIB_LANDMARK_MODEL}. Place it beside app.py."
97
+ )
98
+ _dlib_detector = dlib.get_frontal_face_detector()
99
+ _dlib_predictor = dlib.shape_predictor(DLIB_LANDMARK_MODEL)
100
+ print("[INFO] dlib detector + predictor ready.")
101
+
102
+ # =========================
103
+ # VIDEO: faces + blink features
104
+ # =========================
105
+ def _eye_aspect_ratio(eye_pts):
106
+ A = dist.euclidean(eye_pts[1], eye_pts[5])
107
+ B = dist.euclidean(eye_pts[2], eye_pts[4])
108
+ C = dist.euclidean(eye_pts[0], eye_pts[3])
109
+ return (A + B) / (2.0 * C + 1e-9)
110
+
111
+ def extract_faces_all(path):
112
+ """Sample every 5th frame, keep ALL faces per frame, resize to 224x224."""
113
+ cap = cv2.VideoCapture(path)
114
+ total = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
115
+ if total <= 0:
116
+ cap.release()
117
+ return None
118
+
119
+ faces = []
120
+ for idx in range(0, total, FRAME_STEP):
121
+ if len(faces) >= NUM_MAX_FACES:
122
+ break
123
+ cap.set(cv2.CAP_PROP_POS_FRAMES, idx)
124
+ ok, frame = cap.read()
125
+ if not ok:
126
+ break
127
+ rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
128
+ boxes, _ = _mtcnn.detect(rgb)
129
+ if boxes is None:
130
+ continue
131
+ for (x1, y1, x2, y2) in boxes.astype(int):
132
+ x1, y1 = max(0, x1), max(0, y1)
133
+ x2, y2 = min(frame.shape[1], x2), min(frame.shape[0], y2)
134
+ crop = frame[y1:y2, x1:x2]
135
+ if crop.size > 0:
136
+ faces.append(cv2.resize(crop, IMG_SIZE))
137
+ if len(faces) >= NUM_MAX_FACES:
138
+ break
139
+ cap.release()
140
+ if not faces:
141
+ return None
142
+ return np.stack(faces, axis=0).astype(np.uint8)
143
+
144
+ def blink_features_from_crops(faces):
145
+ """Compute EAR on cropped faces; denominator = #frames with valid EAR."""
146
+ (lStart, lEnd) = face_utils.FACIAL_LANDMARKS_IDXS["left_eye"]
147
+ (rStart, rEnd) = face_utils.FACIAL_LANDMARKS_IDXS["right_eye"]
148
+
149
+ ear_values = []
150
+ blink_counter = 0
151
+ total_blinks = 0
152
+
153
+ for face in faces:
154
+ gray = cv2.cvtColor(face, cv2.COLOR_BGR2GRAY)
155
+ rects = _dlib_detector(gray, 0)
156
+ if len(rects) == 0:
157
+ continue
158
+ shape = _dlib_predictor(gray, rects[0])
159
+ shape = face_utils.shape_to_np(shape)
160
+
161
+ left_eye = shape[lStart:lEnd]
162
+ right_eye = shape[rStart:rEnd]
163
+ ear = 0.5 * (_eye_aspect_ratio(left_eye) + _eye_aspect_ratio(right_eye))
164
+ ear_values.append(ear)
165
+
166
+ if ear < EAR_THRESHOLD:
167
+ blink_counter += 1
168
+ else:
169
+ if blink_counter >= EAR_CONSEC_FRAMES:
170
+ total_blinks += 1
171
+ blink_counter = 0
172
+
173
+ n_ear = len(ear_values)
174
+ if n_ear == 0:
175
+ return np.array([0, 0.0, 0.0], dtype=np.float32)
176
+
177
+ blink_freq = total_blinks / float(n_ear)
178
+ ear_var = float(np.var(np.array(ear_values, dtype=np.float32)))
179
+ return np.array([total_blinks, blink_freq, ear_var], dtype=np.float32)
180
+
181
+ def predict_video_prob(video_path):
182
+ faces = extract_faces_all(video_path)
183
+ if faces is None or len(faces) == 0:
184
+ return None, "No faces detected."
185
+
186
+ blink_feats = blink_features_from_crops(faces)
187
+ # Tile to per-face samples
188
+ tiled = np.tile(blink_feats, (faces.shape[0], 1)).astype(np.float32)
189
+ imgs = resnet_preprocess(faces.astype(np.float32))
190
+
191
+ # Batch size heuristic (bigger if GPU is present)
192
+ has_tf_gpu = len(tf.config.experimental.list_physical_devices("GPU")) > 0
193
+ bs = 128 if has_tf_gpu else 32
194
+
195
+ preds = []
196
+ for i in range(0, imgs.shape[0], bs):
197
+ p = _video_model.predict([imgs[i:i+bs], tiled[i:i+bs]], verbose=0)
198
+ preds.append(p.reshape(-1))
199
+ return float(np.mean(np.concatenate(preds, axis=0))), None
200
+
201
+ def to_verdict(score):
202
+ return "DEEPFAKE" if score >= PRED_THRESHOLD else "REAL"
203
+
204
+ # =========================
205
+ # Inference entry
206
+ # =========================
207
+ def run_inference(video_file):
208
+ lazy_load()
209
+ if video_file is None:
210
+ return None, None, "Please upload a video file."
211
+
212
+ video_prob, vmsg = predict_video_prob(video_file)
213
+ if video_prob is None:
214
+ return None, None, vmsg or "Unable to process the video."
215
+
216
+ verdict = f"VIDEO ONLY: {to_verdict(video_prob)}"
217
+ fmt = lambda x: None if x is None else round(float(x), 4)
218
+ return fmt(video_prob), verdict, None
219
+
220
+ # =========================
221
+ # Gradio UI
222
+ # =========================
223
+ with gr.Blocks(title="Deepfake Detector Video Only (GPU-ready)") as demo:
224
+ gr.Markdown(
225
+ "### 🎭 Deepfake Detector — **Video Only**\n"
226
+ "- **Compute**: Uses GPU automatically if available (PyTorch MTCNN, TensorFlow model; mixed precision on TF).\n"
227
+ "- **Video path**: samples every 5th frame, keeps **all** faces, resizes to 224×224.\n"
228
+ "- **Features**: ResNet50 preprocessing + blink EAR features on cropped faces via dlib.\n"
229
+ f"- **Threshold**: {PRED_THRESHOLD} ( means DEEPFAKE).\n"
230
+ )
231
+ with gr.Row():
232
+ video_in = gr.Video(label="Video")
233
+ go = gr.Button("Analyze")
234
+ with gr.Row():
235
+ v_out = gr.Number(label="Video probability (deepfake)", precision=4)
236
+ verdict_out = gr.Textbox(label="Verdict", interactive=False)
237
+ msg_out = gr.Textbox(label="Message / Warnings", interactive=False)
238
+
239
+ go.click(run_inference, inputs=[video_in], outputs=[v_out, verdict_out, msg_out])
240
+
241
+ if __name__ == "__main__":
242
+ lazy_load()
243
+ demo.launch(server_name="0.0.0.0", server_port=7860)