Spaces:
Sleeping
Sleeping
| """ | |
| facial.py β PsyPredict Facial Emotion Detection Endpoint (FastAPI) | |
| Preserved feature: Keras CNN face emotion model (emotion_engine.py unchanged). | |
| Adapted from Flask Blueprint to FastAPI APIRouter with async file handling. | |
| """ | |
| from __future__ import annotations | |
| import logging | |
| import cv2 | |
| import numpy as np | |
| from fastapi import APIRouter, File, HTTPException, UploadFile | |
| from fastapi.responses import JSONResponse | |
| from app.schemas import EmotionResponse | |
| from app.services.emotion_engine import emotion_detector | |
| logger = logging.getLogger(__name__) | |
| router = APIRouter() | |
| async def predict_emotion(file: UploadFile = File(...)): | |
| """ | |
| Receives an image file and returns detected face emotion + confidence. | |
| Preserved from original implementation β Keras CNN model unchanged. | |
| Gracefully handles empty/corrupt webcam frames without crashing. | |
| """ | |
| if not file.filename: | |
| raise HTTPException(status_code=400, detail="No file selected") | |
| allowed_types = {"image/jpeg", "image/jpg", "image/png", "image/webp"} | |
| if file.content_type not in allowed_types: | |
| raise HTTPException( | |
| status_code=400, | |
| detail=f"Invalid file type '{file.content_type}'. Accepted: JPEG, PNG, WEBP", | |
| ) | |
| try: | |
| contents = await file.read() | |
| # Guard: empty frame (webcam not ready yet) β return neutral silently | |
| if not contents or len(contents) < 100: | |
| return EmotionResponse(emotion="neutral", confidence=0.0, message="Empty frame skipped") | |
| if len(contents) > 10 * 1024 * 1024: # 10 MB limit | |
| raise HTTPException(status_code=413, detail="Image too large (max 10MB)") | |
| # Decode to OpenCV format in memory (no disk I/O) | |
| file_bytes = np.frombuffer(contents, np.uint8) | |
| if file_bytes.size == 0: | |
| return EmotionResponse(emotion="neutral", confidence=0.0, message="Empty buffer") | |
| image = cv2.imdecode(file_bytes, cv2.IMREAD_COLOR) | |
| # Guard: corrupted/blank frame β return neutral instead of crashing | |
| if image is None: | |
| return EmotionResponse(emotion="neutral", confidence=0.0, message="Camera frame not ready") | |
| result = emotion_detector.detect_emotion(image) | |
| if "error" in result: | |
| # No face detected β return neutral without crashing | |
| return EmotionResponse(emotion="neutral", confidence=0.0, message=result.get("error")) | |
| return EmotionResponse(**result) | |
| except HTTPException: | |
| raise | |
| except Exception as exc: | |
| # Log at DEBUG level to reduce terminal noise during normal webcam polling | |
| logger.debug("Facial emotion prediction skipped: %s", exc) | |
| return EmotionResponse(emotion="neutral", confidence=0.0, message="Frame processing error") |