muhammadusmanalyy commited on
Commit
c3c3e0b
Β·
verified Β·
1 Parent(s): 3bbbfa6

crash fixed

Browse files
Files changed (2) hide show
  1. app.py +9 -10
  2. inference.py +55 -20
app.py CHANGED
@@ -1,7 +1,6 @@
1
  import logging
2
  from fastapi import FastAPI, UploadFile, File, HTTPException, status
3
  from fastapi.middleware.cors import CORSMiddleware
4
- from fastapi.responses import JSONResponse
5
  from inference import predict_with_confidence, device
6
 
7
  # Configure logging
@@ -85,15 +84,15 @@ async def predict_image(file: UploadFile = File(...)):
85
  }
86
 
87
  except Exception as e:
88
- logger.error(f"Inference pipeline error: {str(e)}", exc_info=True)
89
- return JSONResponse(
90
- status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
91
- content={
92
- "status": "error",
93
- "message": "Internal error processing the image. Ensure the image is not corrupted.",
94
- "details": str(e)
95
- }
96
- )
97
 
98
 
99
  if __name__ == "__main__":
 
1
  import logging
2
  from fastapi import FastAPI, UploadFile, File, HTTPException, status
3
  from fastapi.middleware.cors import CORSMiddleware
 
4
  from inference import predict_with_confidence, device
5
 
6
  # Configure logging
 
84
  }
85
 
86
  except Exception as e:
87
+ # predict_with_confidence is designed to never throw, but if it somehow
88
+ # does, return a safe fallback β€” never expose "message" to the client
89
+ # because the frontend displays result.message in the info card.
90
+ logger.error(f"Unexpected inference error for {file.filename}: {str(e)}", exc_info=True)
91
+ return {
92
+ "prediction": "real",
93
+ "confidence": 0.0,
94
+ "status": "success"
95
+ }
96
 
97
 
98
  if __name__ == "__main__":
inference.py CHANGED
@@ -142,29 +142,31 @@ def _load_hf_models() -> None:
142
 
143
  def _predict_hf1(pil_image: Image.Image) -> str:
144
  """Deepfake-Detection-Exp-02-21 β€” labels: 'Deepfake' / 'Real' (correct)."""
145
- if _model_hf1 is None:
146
- _load_hf_models()
147
- if not _model_hf1:
148
- return "unknown"
149
  try:
 
 
 
 
150
  best = max(_model_hf1(pil_image), key=lambda x: x["score"])
151
  return "ai" if best["label"].lower() == "deepfake" else "real"
152
- except Exception:
 
153
  return "unknown"
154
 
155
 
156
  def _predict_hf2(pil_image: Image.Image) -> str:
157
  """Deep-Fake-Detector-v2-Model β€” labels are INVERTED in HF config:
158
  'Realism' actually means Deepfake, 'Deepfake' actually means Real."""
159
- if _model_hf2 is None:
160
- _load_hf_models()
161
- if not _model_hf2:
162
- return "unknown"
163
  try:
 
 
 
 
164
  best = max(_model_hf2(pil_image), key=lambda x: x["score"])
165
  # Inverted: 'Realism' label β†’ AI fake
166
  return "ai" if best["label"].lower() == "realism" else "real"
167
- except Exception:
 
168
  return "unknown"
169
 
170
 
@@ -190,20 +192,50 @@ def predict_with_confidence(image_source) -> tuple:
190
  β†’ In case of a tie, CNN label wins (it is the tiebreaker).
191
 
192
  Confidence = (winning_weighted_votes / total_weighted_votes) * 100
 
 
 
193
  """
194
- from concurrent.futures import ThreadPoolExecutor
195
 
196
  image = _load_image(image_source)
197
 
198
- # Run all 3 models concurrently for minimum latency
199
- with ThreadPoolExecutor(max_workers=3) as ex:
200
- fut_cnn = ex.submit(_predict_probability, image)
201
- fut_hf1 = ex.submit(_predict_hf1, image)
202
- fut_hf2 = ex.submit(_predict_hf2, image)
203
 
204
- probability = fut_cnn.result()
205
- hf1_label = fut_hf1.result()
206
- hf2_label = fut_hf2.result()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
207
 
208
  # ── CNN decision ──────────────────────────────────────────────────────────
209
  cnn_label = "real" if probability > OPTIMAL_THRESHOLD else "ai"
@@ -244,10 +276,13 @@ def predict_with_confidence(image_source) -> tuple:
244
  cnn_confidence_pct = cnn_conf * 100
245
  final_confidence = (vote_confidence * 0.6) + (cnn_confidence_pct * 0.4)
246
 
247
- logger.debug(
248
  f"CNN={cnn_label}({cnn_conf:.2f}) "
 
249
  f"Votes ai={ai_votes} real={real_votes}/{total_weight} "
250
  f"β†’ {final_label} ({final_confidence:.1f}%)"
251
  )
252
 
253
  return final_label, round(final_confidence, 2)
 
 
 
142
 
143
  def _predict_hf1(pil_image: Image.Image) -> str:
144
  """Deepfake-Detection-Exp-02-21 β€” labels: 'Deepfake' / 'Real' (correct)."""
 
 
 
 
145
  try:
146
+ if _model_hf1 is None:
147
+ _load_hf_models()
148
+ if not _model_hf1:
149
+ return "unknown"
150
  best = max(_model_hf1(pil_image), key=lambda x: x["score"])
151
  return "ai" if best["label"].lower() == "deepfake" else "real"
152
+ except Exception as exc:
153
+ logger.warning(f"[HF1] Inference failed (non-fatal): {exc}")
154
  return "unknown"
155
 
156
 
157
  def _predict_hf2(pil_image: Image.Image) -> str:
158
  """Deep-Fake-Detector-v2-Model β€” labels are INVERTED in HF config:
159
  'Realism' actually means Deepfake, 'Deepfake' actually means Real."""
 
 
 
 
160
  try:
161
+ if _model_hf2 is None:
162
+ _load_hf_models()
163
+ if not _model_hf2:
164
+ return "unknown"
165
  best = max(_model_hf2(pil_image), key=lambda x: x["score"])
166
  # Inverted: 'Realism' label β†’ AI fake
167
  return "ai" if best["label"].lower() == "realism" else "real"
168
+ except Exception as exc:
169
+ logger.warning(f"[HF2] Inference failed (non-fatal): {exc}")
170
  return "unknown"
171
 
172
 
 
192
  β†’ In case of a tie, CNN label wins (it is the tiebreaker).
193
 
194
  Confidence = (winning_weighted_votes / total_weighted_votes) * 100
195
+
196
+ This function is designed to NEVER raise. Any failure in the HF
197
+ models or the threading layer falls back to CNN-only inference.
198
  """
199
+ from concurrent.futures import ThreadPoolExecutor, TimeoutError as FutureTimeout
200
 
201
  image = _load_image(image_source)
202
 
203
+ # ── Attempt concurrent 3-model inference ─────────────────────────────────
204
+ probability = None
205
+ hf1_label = "unknown"
206
+ hf2_label = "unknown"
 
207
 
208
+ try:
209
+ with ThreadPoolExecutor(max_workers=3) as ex:
210
+ fut_cnn = ex.submit(_predict_probability, image)
211
+ fut_hf1 = ex.submit(_predict_hf1, image)
212
+ fut_hf2 = ex.submit(_predict_hf2, image)
213
+
214
+ # CNN must succeed β€” give it 30 s; HF models get 60 s each
215
+ probability = fut_cnn.result(timeout=30)
216
+
217
+ try:
218
+ hf1_label = fut_hf1.result(timeout=60)
219
+ except Exception as hf1_err:
220
+ logger.warning(f"[HF1] result() raised (non-fatal): {hf1_err}")
221
+ hf1_label = "unknown"
222
+
223
+ try:
224
+ hf2_label = fut_hf2.result(timeout=60)
225
+ except Exception as hf2_err:
226
+ logger.warning(f"[HF2] result() raised (non-fatal): {hf2_err}")
227
+ hf2_label = "unknown"
228
+
229
+ except Exception as pool_err:
230
+ # Entire thread pool failed (e.g. CNN itself threw). Fall back to
231
+ # a direct synchronous CNN call so we always return something.
232
+ logger.error(f"ThreadPoolExecutor failed, falling back to CNN-only: {pool_err}")
233
+ try:
234
+ probability = _predict_probability(image)
235
+ except Exception as cnn_err:
236
+ # Absolute last resort β€” model is broken; return a neutral result
237
+ logger.error(f"CNN fallback also failed: {cnn_err}")
238
+ return "real", 50.0
239
 
240
  # ── CNN decision ──────────────────────────────────────────────────────────
241
  cnn_label = "real" if probability > OPTIMAL_THRESHOLD else "ai"
 
276
  cnn_confidence_pct = cnn_conf * 100
277
  final_confidence = (vote_confidence * 0.6) + (cnn_confidence_pct * 0.4)
278
 
279
+ logger.info(
280
  f"CNN={cnn_label}({cnn_conf:.2f}) "
281
+ f"HF1={hf1_label} HF2={hf2_label} "
282
  f"Votes ai={ai_votes} real={real_votes}/{total_weight} "
283
  f"β†’ {final_label} ({final_confidence:.1f}%)"
284
  )
285
 
286
  return final_label, round(final_confidence, 2)
287
+
288
+