Files changed (1) hide show
  1. app.py +49 -26
app.py CHANGED
@@ -4,6 +4,10 @@ import torch.nn as nn
4
  import sys
5
  import os
6
 
 
 
 
 
7
  sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
8
 
9
  from models.audio_model import FrozenExtractor, LinearProjection, AGRU, PersonalityRegressor
@@ -98,7 +102,6 @@ else:
98
  proj_state = {k[len("proj."):]: v for k, v in audio_state.items() if k.startswith("proj.")}
99
  agru_state = {k[len("agru."):]: v for k, v in audio_state.items() if k.startswith("agru.")}
100
  reg_state = {k[len("regressor."):]: v for k, v in audio_state.items() if k.startswith("regressor.")}
101
-
102
  if dt_state:
103
  deep_transformer.load_state_dict(dt_state)
104
  proj.load_state_dict(proj_state)
@@ -114,56 +117,76 @@ print("⏳ Loading face detector...")
114
  face_detector = _load_face_detector()
115
  print("βœ“ Face detector ready")
116
 
117
- # ── Inference ──────────────────────────────────────────────────────────────────
118
- def predict(video_path):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
119
  if video_path is None:
120
  return None, "⚠️ Please upload a video first."
121
-
122
  try:
123
  with open(video_path, "rb") as f:
124
  video_bytes = f.read()
125
-
126
- visual_preds, visual_emb = get_visual_embedding(video_bytes, visual_model, DEVICE, face_detector)
127
- audio_preds, audio_emb = get_audio_embedding(
128
- video_bytes, frozen_ext, deep_transformer, proj, agru, regressor, DEVICE
129
- )
130
- fusion_preds = get_fusion_prediction(audio_emb, visual_emb, fusion_model, DEVICE)
131
-
132
- results = format_predictions(fusion_preds, visual_preds, audio_preds)
133
- fusion = results["fusion"]
134
 
 
 
 
135
  md = "## 🧠 Fusion Results (Main)\n\n"
136
  for trait, score in fusion.items():
137
  bar = "β–ˆ" * int(score / 5) + "β–‘" * (20 - int(score / 5))
138
  md += f"**{trait}** `{score:.1f}%` {bar}\n\n"
139
-
140
  return results, md
141
-
142
  except Exception as e:
143
  import traceback
144
  return None, f"❌ Error: {str(e)}\n\n```{traceback.format_exc()}```"
145
 
146
- # ── Gradio UI ──────────────────────────────────────────────────────────────────
147
- with gr.Blocks(title="Personality Prediction API") as demo:
148
-
149
- gr.Markdown("# 🧠 Personality Prediction\nUpload a video to predict **OCEAN** personality traits.")
150
-
151
  with gr.Row():
152
  with gr.Column(scale=1):
153
  video_input = gr.Video(label="πŸ“Ή Upload Video")
154
  predict_btn = gr.Button("πŸ” Predict Personality", variant="primary", size="lg")
155
-
156
  with gr.Column(scale=1):
157
  summary_out = gr.Markdown(label="Results Summary")
158
-
159
  with gr.Accordion("πŸ“Š Full Results", open=False):
160
  json_out = gr.JSON(label="Raw Scores (%)")
161
 
162
  predict_btn.click(
163
- fn=predict,
164
  inputs=video_input,
165
- outputs=[json_out, summary_out],
166
- api_name="predict" # This creates the API endpoint
167
  )
168
 
169
- demo.launch()
 
 
4
  import sys
5
  import os
6
 
7
+ from fastapi import FastAPI, UploadFile, File
8
+ from fastapi.middleware.cors import CORSMiddleware
9
+ from fastapi.responses import JSONResponse
10
+
11
  sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
12
 
13
  from models.audio_model import FrozenExtractor, LinearProjection, AGRU, PersonalityRegressor
 
102
  proj_state = {k[len("proj."):]: v for k, v in audio_state.items() if k.startswith("proj.")}
103
  agru_state = {k[len("agru."):]: v for k, v in audio_state.items() if k.startswith("agru.")}
104
  reg_state = {k[len("regressor."):]: v for k, v in audio_state.items() if k.startswith("regressor.")}
 
105
  if dt_state:
106
  deep_transformer.load_state_dict(dt_state)
107
  proj.load_state_dict(proj_state)
 
117
  face_detector = _load_face_detector()
118
  print("βœ“ Face detector ready")
119
 
120
+
121
+ # ── CORE LOGIC HELPER ──────────────────────────────────────────────────────────
122
+ def process_video_bytes(video_bytes):
123
+ visual_preds, visual_emb = get_visual_embedding(video_bytes, visual_model, DEVICE, face_detector)
124
+ audio_preds, audio_emb = get_audio_embedding(video_bytes, frozen_ext, deep_transformer, proj, agru, regressor, DEVICE)
125
+ fusion_preds = get_fusion_prediction(audio_emb, visual_emb, fusion_model, DEVICE)
126
+ results = format_predictions(fusion_preds, visual_preds, audio_preds)
127
+ return results
128
+
129
+
130
+ # ── FASTAPI SETUP (FOR REACT) ──────────────────────────────────────────────────
131
+ app = FastAPI()
132
+
133
+ # THE MAGIC BULLET FOR CORS: allow_origins=["*"] MUST HAVE allow_credentials=False
134
+ app.add_middleware(
135
+ CORSMiddleware,
136
+ allow_origins=["*"],
137
+ allow_credentials=False,
138
+ allow_methods=["*"],
139
+ allow_headers=["*"],
140
+ )
141
+
142
+ @app.post("/api/predict")
143
+ async def api_predict(video: UploadFile = File(...)):
144
+ """This route is exclusively for your React app to call via Axios"""
145
+ try:
146
+ video_bytes = await video.read()
147
+ results = process_video_bytes(video_bytes)
148
+ return JSONResponse(content=results)
149
+ except Exception as e:
150
+ import traceback
151
+ return JSONResponse(content={"error": str(e), "trace": traceback.format_exc()}, status_code=500)
152
+
153
+
154
+ # ── GRADIO UI SETUP (FOR MANUAL TESTING) ───────────────────────────────────────
155
+ def gradio_predict(video_path):
156
  if video_path is None:
157
  return None, "⚠️ Please upload a video first."
 
158
  try:
159
  with open(video_path, "rb") as f:
160
  video_bytes = f.read()
 
 
 
 
 
 
 
 
 
161
 
162
+ results = process_video_bytes(video_bytes)
163
+
164
+ fusion = results["fusion"]
165
  md = "## 🧠 Fusion Results (Main)\n\n"
166
  for trait, score in fusion.items():
167
  bar = "β–ˆ" * int(score / 5) + "β–‘" * (20 - int(score / 5))
168
  md += f"**{trait}** `{score:.1f}%` {bar}\n\n"
 
169
  return results, md
 
170
  except Exception as e:
171
  import traceback
172
  return None, f"❌ Error: {str(e)}\n\n```{traceback.format_exc()}```"
173
 
174
+ with gr.Blocks(title="Personality Prediction API", theme=gr.themes.Soft()) as demo:
175
+ gr.Markdown("# 🧠 Personality Prediction")
 
 
 
176
  with gr.Row():
177
  with gr.Column(scale=1):
178
  video_input = gr.Video(label="πŸ“Ή Upload Video")
179
  predict_btn = gr.Button("πŸ” Predict Personality", variant="primary", size="lg")
 
180
  with gr.Column(scale=1):
181
  summary_out = gr.Markdown(label="Results Summary")
 
182
  with gr.Accordion("πŸ“Š Full Results", open=False):
183
  json_out = gr.JSON(label="Raw Scores (%)")
184
 
185
  predict_btn.click(
186
+ fn=gradio_predict,
187
  inputs=video_input,
188
+ outputs=[json_out, summary_out]
 
189
  )
190
 
191
+ # Mount Gradio over the FastAPI app
192
+ app = gr.mount_gradio_app(app, demo, path="/")