Spaces:
Running
Running
| # app.py | |
| from fastapi import FastAPI, UploadFile, File | |
| from fastapi.responses import JSONResponse, FileResponse | |
| from fastapi.middleware.cors import CORSMiddleware | |
| import time, json, base64 | |
| import cv2, numpy as np | |
| from PIL import Image | |
| from io import BytesIO | |
| import gradio as gr | |
| # Optional YOLO & FER | |
| try: | |
| from ultralytics import YOLO | |
| YOLO_MODEL = YOLO("yolov8n.pt") | |
| except Exception: | |
| YOLO_MODEL = None | |
| try: | |
| from fer import FER | |
| EMO_MODEL = FER(mtcnn=True) | |
| except Exception: | |
| EMO_MODEL = None | |
| app = FastAPI(title="GYaaNa SAroVar(GYSV) Interview Monitor Backend") | |
| app.add_middleware( | |
| CORSMiddleware, | |
| allow_origins=["*"], | |
| allow_credentials=True, | |
| allow_methods=["*"], | |
| allow_headers=["*"] | |
| ) | |
| STORE = {"frames": [], "texts": [], "analysis": None, "meta_start": time.time()} | |
| FILLERS = {"um","uh","like","you know","so","actually","basically","right","okay"} | |
| def text_metrics(text): | |
| words = len(text.split()) | |
| fillers = sum(text.lower().count(f) for f in FILLERS) | |
| elapsed_min = max(1/60, (time.time() - STORE.get("meta_start", time.time())) / 60) | |
| wpm = round(words / elapsed_min, 2) | |
| quality = max(0, 100 - fillers*5) | |
| return {"fillers": fillers, "wpm": wpm, "text_quality": quality, "technical_score": quality} | |
| def decode_frame(b64str): | |
| b64data = b64str.split(",")[1] if "," in b64str else b64str | |
| img_data = base64.b64decode(b64data) | |
| img = np.array(Image.open(BytesIO(img_data))) | |
| if img.shape[2] == 4: | |
| img = cv2.cvtColor(img, cv2.COLOR_RGBA2RGB) | |
| return img | |
| async def analyze_frame(payload: dict): | |
| frame_b64 = payload.get("frame", "") | |
| transcript = payload.get("transcript", "") | |
| ts = time.time() | |
| img = decode_frame(frame_b64) if frame_b64 else None | |
| # Person detection | |
| person_present = False | |
| boxes = [] | |
| if img is not None and YOLO_MODEL: | |
| results = YOLO_MODEL(img) | |
| for r in results: | |
| for det in r.boxes.xyxy: | |
| boxes.append({"x": int(det[0]), "y": int(det[1]), "w": int(det[2]-det[0]), "h": int(det[3]-det[1]), "label": "person"}) | |
| person_present = len(boxes) > 0 | |
| # Expression detection | |
| expression = {"label": "neutral", "conf": 0.6} | |
| if img is not None and EMO_MODEL: | |
| em_res = EMO_MODEL.detect_emotions(img) | |
| if em_res: | |
| expression = em_res[0]["emotions"] | |
| metrics = text_metrics(transcript) | |
| analysis = { | |
| "time": ts, | |
| "person_present": person_present, | |
| "expression": expression, | |
| "boxes": boxes, | |
| "dress_style": "shirt (unisex)", | |
| "live_transcript": transcript or "No speech yet.", | |
| "metrics": metrics, | |
| "technical_score": metrics.get("technical_score"), | |
| "final_analysis": "Simulated live analysis" | |
| } | |
| STORE["analysis"] = analysis | |
| return JSONResponse(analysis) | |
| async def upload_audio(audio: UploadFile = File(...)): | |
| return JSONResponse({"message": "Audio received. Transcription can be integrated with Whisper or other ASR models."}) | |
| async def final_analysis(): | |
| return JSONResponse(STORE.get("analysis", {})) | |
| async def download_json(): | |
| data = json.dumps(STORE, indent=2).encode("utf-8") | |
| path = "/tmp/export_analysis.json" | |
| with open(path,"wb") as f: f.write(data) | |
| return FileResponse(path, media_type="application/json", filename="analysis.json") | |
| # ------------------- Gradio Frontend ------------------- | |
| def analyze_audio(audio): | |
| if audio is None: | |
| return "No audio recorded" | |
| # Compute transcript & metrics (mock example) | |
| duration = len(audio[1])/audio[0] | |
| text = f"Audio received ({duration:.2f}s)" | |
| metrics = text_metrics(text) | |
| return f"{text}\nMetrics: {metrics}" | |
| with gr.Blocks() as demo: | |
| gr.Markdown("## GYaaNa SAroVar (GYSV) Interview Monitor") | |
| with gr.Row(): | |
| video_output = gr.Video(source="webcam", streaming=True, label="Camera") | |
| audio_input = gr.Audio(source="microphone", type="numpy", label="Record your voice") | |
| output_text = gr.Textbox(label="Transcript / Analysis") | |
| audio_input.change(analyze_audio, inputs=audio_input, outputs=output_text) | |
| # Only run frontend when script executed | |
| if __name__ == "__main__": | |
| demo.launch() | |