spideyhead commited on
Commit
1ab031c
Β·
verified Β·
1 Parent(s): 4d5d7cd

Upload main.py

Browse files
Files changed (1) hide show
  1. main.py +58 -35
main.py CHANGED
@@ -61,16 +61,16 @@ def extract_clip(video_path, start_frame):
61
  """
62
  Open a fresh cap, seek ONCE to start_frame, then read frames
63
  SEQUENTIALLY (no cap.set inside loop). This is reliable for all codecs.
64
- Collects SEQUENCE_LENGTH face crops.
65
  """
66
  cap = cv2.VideoCapture(video_path)
67
  cap.set(cv2.CAP_PROP_POS_FRAMES, start_frame) # Seek exactly ONCE
68
 
69
  faces = []
70
- last_box = None
71
  attempts = 0
72
 
73
- while len(faces) < SEQUENCE_LENGTH and attempts < 300:
74
  ret, frame = cap.read() # Sequential β€” no random seeking
75
  if not ret:
76
  break
@@ -79,19 +79,25 @@ def extract_clip(video_path, start_frame):
79
  frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
80
  pil_img = Image.fromarray(frame_rgb)
81
 
82
- boxes, _ = mtcnn.detect(pil_img)
83
-
84
- if boxes is not None and len(boxes) > 0:
85
- smoothed = smooth_box(boxes[0].tolist(), last_box)
86
- last_box = smoothed
87
- elif last_box is not None:
88
- smoothed = last_box # Hold last known position
89
- else:
90
- continue # No face yet β€” keep reading
91
-
92
- crop = crop_face(pil_img, smoothed)
93
- if crop is not None:
94
- faces.append(crop)
 
 
 
 
 
 
95
 
96
  cap.release()
97
 
@@ -153,28 +159,45 @@ async def analyze_video(file: UploadFile = File(...)):
153
  return {"isFake": False, "confidence": 0,
154
  "explanation": "Could not read the video file.", "details": []}
155
 
156
- # ── Start from the middle of the video (most likely to have a clear face) ─
157
- start_frame = max(0, (total_frames // 2) - (SEQUENCE_LENGTH // 2))
158
- print(f"Extracting clip from frame {start_frame}...")
159
-
160
- faces = extract_clip(temp_path, start_frame)
161
-
162
- if not faces:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
163
  return {
164
  "isFake": False, "confidence": 0,
165
  "explanation": "Could not detect a clear face in the video. Ensure the subject's face is visible.",
166
  "details": []
167
  }
168
 
169
- print(f"Extracted {len(faces)} face frames. Running inference...")
170
- fake_prob = run_inference(faces)
171
-
172
  # ── Apply calibrated threshold ─────────────────────────────────────────
173
  # This model scores ~60-75% for real videos, so we require 80%+ to call FAKE.
174
- is_fake = fake_prob >= FAKE_THRESHOLD
175
- confidence = round((fake_prob if is_fake else 1.0 - fake_prob) * 100, 2)
176
 
177
- print(f"FINAL β€” fake_prob={fake_prob:.3f}, threshold={FAKE_THRESHOLD}, isFake={is_fake}, confidence={confidence}%")
178
 
179
  explanation = (
180
  "Our Temporal AI detected strong evidence of facial manipulation β€” "
@@ -182,7 +205,7 @@ async def analyze_video(file: UploadFile = File(...)):
182
  "characteristic of deepfake synthesis."
183
  if is_fake else
184
  "Our Temporal AI found no significant manipulation artifacts. "
185
- "Facial motion, micro-expressions, and temporal consistency appear natural."
186
  )
187
 
188
  return {
@@ -191,16 +214,16 @@ async def analyze_video(file: UploadFile = File(...)):
191
  "explanation": explanation,
192
  "details": [
193
  {
194
- "title": "Temporal Analysis",
195
- "desc": f"Analyzed a 16-frame consecutive clip from the middle of the video using a VideoMAE 3D Transformer."
196
  },
197
  {
198
- "title": "Sequential Face Tracking",
199
- "desc": "Frames read sequentially with EMA bounding-box smoothing for stable face tracking."
200
  },
201
  {
202
  "title": "Calibrated Threshold",
203
- "desc": f"Raw model score: {fake_prob*100:.1f}%. Detection threshold: {FAKE_THRESHOLD*100:.0f}% (tuned to reduce false positives)."
204
  }
205
  ]
206
  }
 
61
  """
62
  Open a fresh cap, seek ONCE to start_frame, then read frames
63
  SEQUENTIALLY (no cap.set inside loop). This is reliable for all codecs.
64
+ Collects SEQUENCE_LENGTH face crops using bounding box caching for speed.
65
  """
66
  cap = cv2.VideoCapture(video_path)
67
  cap.set(cv2.CAP_PROP_POS_FRAMES, start_frame) # Seek exactly ONCE
68
 
69
  faces = []
70
+ base_box = None
71
  attempts = 0
72
 
73
+ while len(faces) < SEQUENCE_LENGTH and attempts < 60:
74
  ret, frame = cap.read() # Sequential β€” no random seeking
75
  if not ret:
76
  break
 
79
  frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
80
  pil_img = Image.fromarray(frame_rgb)
81
 
82
+ if base_box is None:
83
+ # Downscale for faster MTCNN detection
84
+ detect_img = pil_img.copy()
85
+ if detect_img.width > 640:
86
+ ratio = 640.0 / detect_img.width
87
+ detect_img = detect_img.resize((640, int(detect_img.height * ratio)))
88
+ boxes, _ = mtcnn.detect(detect_img)
89
+ if boxes is not None and len(boxes) > 0:
90
+ base_box = [b / ratio for b in boxes[0].tolist()]
91
+ else:
92
+ boxes, _ = mtcnn.detect(pil_img)
93
+ if boxes is not None and len(boxes) > 0:
94
+ base_box = boxes[0].tolist()
95
+
96
+ if base_box is not None:
97
+ # We use a larger padding (0.5) because we are caching the box and the face might move slightly
98
+ crop = crop_face(pil_img, base_box, padding=0.5)
99
+ if crop is not None:
100
+ faces.append(crop)
101
 
102
  cap.release()
103
 
 
159
  return {"isFake": False, "confidence": 0,
160
  "explanation": "Could not read the video file.", "details": []}
161
 
162
+ # ── Extract multiple clips from different parts of the video ───────────────
163
+ segments = [
164
+ max(0, int(total_frames * 0.2) - (SEQUENCE_LENGTH // 2)),
165
+ max(0, int(total_frames * 0.5) - (SEQUENCE_LENGTH // 2)),
166
+ max(0, int(total_frames * 0.8) - (SEQUENCE_LENGTH // 2))
167
+ ]
168
+
169
+ # Ensure unique starting frames if the video is very short
170
+ segments = sorted(list(set(segments)))
171
+
172
+ highest_fake_prob = 0.0
173
+ successful_clips = 0
174
+
175
+ for start_frame in segments:
176
+ print(f"Extracting clip from frame {start_frame}...")
177
+ faces = extract_clip(temp_path, start_frame)
178
+
179
+ if faces:
180
+ prob = run_inference(faces)
181
+ highest_fake_prob = max(highest_fake_prob, prob)
182
+ successful_clips += 1
183
+
184
+ # If we already found very strong evidence of a fake, we can short-circuit to save time
185
+ if highest_fake_prob > 0.95:
186
+ break
187
+
188
+ if successful_clips == 0:
189
  return {
190
  "isFake": False, "confidence": 0,
191
  "explanation": "Could not detect a clear face in the video. Ensure the subject's face is visible.",
192
  "details": []
193
  }
194
 
 
 
 
195
  # ── Apply calibrated threshold ─────────────────────────────────────────
196
  # This model scores ~60-75% for real videos, so we require 80%+ to call FAKE.
197
+ is_fake = highest_fake_prob >= FAKE_THRESHOLD
198
+ confidence = round((highest_fake_prob if is_fake else 1.0 - highest_fake_prob) * 100, 2)
199
 
200
+ print(f"FINAL β€” fake_prob={highest_fake_prob:.3f}, threshold={FAKE_THRESHOLD}, isFake={is_fake}, confidence={confidence}%")
201
 
202
  explanation = (
203
  "Our Temporal AI detected strong evidence of facial manipulation β€” "
 
205
  "characteristic of deepfake synthesis."
206
  if is_fake else
207
  "Our Temporal AI found no significant manipulation artifacts. "
208
+ "Facial motion, micro-expressions, and temporal consistency appear natural across the analyzed segments."
209
  )
210
 
211
  return {
 
214
  "explanation": explanation,
215
  "details": [
216
  {
217
+ "title": "Multi-Segment Temporal Analysis",
218
+ "desc": f"Analyzed {successful_clips} separate 16-frame consecutive clips across the video using a VideoMAE 3D Transformer."
219
  },
220
  {
221
+ "title": "Optimized Face Tracking",
222
+ "desc": "Frames read sequentially with bounding-box caching and downscaled initial detection for extremely fast analysis."
223
  },
224
  {
225
  "title": "Calibrated Threshold",
226
+ "desc": f"Peak raw model score: {highest_fake_prob*100:.1f}%. Detection threshold: {FAKE_THRESHOLD*100:.0f}% (tuned to reduce false positives)."
227
  }
228
  ]
229
  }