Spaces:
Build error
Build error
| import cv2 | |
| import dlib | |
| import numpy as np | |
| import base64 | |
| from fastapi import FastAPI, WebSocket | |
| from scipy.spatial import distance as dist | |
| from imutils import face_utils | |
| import uvicorn | |
| import json | |
| app = FastAPI() | |
| import os | |
| # --- CONFIGURATION FOR HUGGING FACE --- | |
| # Try multiple paths for the model file | |
| possible_paths = [ | |
| "shape_predictor_68_face_landmarks.dat", | |
| "data/models/shape_predictor_68_face_landmarks.dat", | |
| "/app/shape_predictor_68_face_landmarks.dat", | |
| "/app/data/models/shape_predictor_68_face_landmarks.dat" | |
| ] | |
| predictor_path = None | |
| for path in possible_paths: | |
| if os.path.exists(path): | |
| predictor_path = path | |
| break | |
| if not predictor_path: | |
| print("[ERROR] shape_predictor_68_face_landmarks.dat not found in any of the searched paths.") | |
| print(f"Current working directory: {os.getcwd()}") | |
| print(f"Files in current directory: {os.listdir('.')}") | |
| else: | |
| print(f"[INFO] Using model at: {predictor_path}") | |
| print("[INFO] Loading AI models...") | |
| detector = dlib.get_frontal_face_detector() | |
| try: | |
| if predictor_path: | |
| predictor = dlib.shape_predictor(predictor_path) | |
| print("[SUCCESS] Model loaded!") | |
| else: | |
| print("[ERROR] Predictor path is None, cannot load model.") | |
| except Exception as e: | |
| print(f"[ERROR] Failed to load model: {e}") | |
| # Landmark indexes | |
| (lStart, lEnd) = face_utils.FACIAL_LANDMARKS_IDXS["left_eye"] | |
| (rStart, rEnd) = face_utils.FACIAL_LANDMARKS_IDXS["right_eye"] | |
| (mStart, mEnd) = face_utils.FACIAL_LANDMARKS_IDXS["mouth"] | |
| (nStart, nEnd) = face_utils.FACIAL_LANDMARKS_IDXS["nose"] | |
| def get_ear(eye): | |
| a = dist.euclidean(eye[1], eye[5]) | |
| b = dist.euclidean(eye[2], eye[4]) | |
| c = dist.euclidean(eye[0], eye[3]) | |
| return (a + b) / (2.0 * c) | |
| def get_mar(mouth): | |
| a = dist.euclidean(mouth[13], mouth[19]) | |
| b = dist.euclidean(mouth[14], mouth[18]) | |
| c = dist.euclidean(mouth[15], mouth[17]) | |
| d = dist.euclidean(mouth[12], mouth[16]) | |
| return (a + b + c) / (2.0 * d + 1e-6) | |
| async def websocket_endpoint(websocket: WebSocket): | |
| await websocket.accept() | |
| counter = 0 | |
| print("[INFO] Mobile App connected!") | |
| try: | |
| while True: | |
| data = await websocket.receive_text() | |
| header, encoded = data.split(",", 1) | |
| nparr = np.frombuffer(base64.b64decode(encoded), np.uint8) | |
| frame = cv2.imdecode(nparr, cv2.IMREAD_COLOR) | |
| if frame is None: | |
| continue | |
| gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) | |
| faces = detector(gray, 0) | |
| response = { | |
| "drowsy": False, | |
| "distracted": False, | |
| "yawning": False, | |
| "ear": 0, | |
| "mar": 0 | |
| } | |
| for rect in faces: | |
| shape = predictor(gray, rect) | |
| shape = face_utils.shape_to_np(shape) | |
| # Detection Logic | |
| ear = (get_ear(shape[lStart:lEnd]) + get_ear(shape[rStart:rEnd])) / 2.0 | |
| mar = get_mar(shape[mStart:mEnd]) | |
| nose_center = shape[nStart:nEnd].mean(axis=0) | |
| dist_left = dist.euclidean(shape[lStart:lEnd].mean(axis=0), nose_center) | |
| dist_right = dist.euclidean(shape[rStart:rEnd].mean(axis=0), nose_center) | |
| gaze_ratio = dist_left / (dist_right + 1e-6) | |
| response["ear"] = round(ear, 3) | |
| response["mar"] = round(mar, 3) | |
| if ear < 0.22: counter += 1 | |
| else: counter = 0 | |
| if counter >= 5: response["drowsy"] = True | |
| if gaze_ratio < 0.7 or gaze_ratio > 1.3: response["distracted"] = True | |
| if mar > 0.6: response["yawning"] = True | |
| await websocket.send_json(response) | |
| except Exception as e: | |
| print(f"[ERROR] Connection closed: {e}") | |
| if __name__ == "__main__": | |
| # Port 7860 is required for Hugging Face Spaces | |
| uvicorn.run(app, host="0.0.0.0", port=7860) | |