Spaces:
Runtime error
Runtime error
| import streamlit as st | |
| import cv2 | |
| import mediapipe as mp | |
| import numpy as np | |
| from gtts import gTTS | |
| from playsound import playsound | |
| import os | |
| import time | |
| import threading | |
| # Configuration | |
| st.set_page_config(layout="wide") | |
| st.title("🧠 Assistant de Rééducation - Analyse de Posture") | |
| # Sélection de l'exercice | |
| selected_exercise = st.selectbox("Choisissez un exercice :", ["Bras levés", "Squat", "Inclinaison latérale"]) | |
| # Initialisation | |
| mp_drawing = mp.solutions.drawing_utils | |
| mp_pose = mp.solutions.pose | |
| cap = cv2.VideoCapture(0) | |
| cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640) | |
| cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480) | |
| stframe = st.empty() | |
| feedback_placeholder = st.empty() | |
| previous_feedback = "" | |
| last_speech_time = 0 | |
| MIN_DELAY = 3 # secondes | |
| def speak_async(text): | |
| def _speak(): | |
| tts = gTTS(text=text, lang='fr') | |
| filename = f"feedback_{int(time.time())}.mp3" | |
| tts.save(filename) | |
| playsound(filename) | |
| os.remove(filename) | |
| threading.Thread(target=_speak).start() | |
| with mp_pose.Pose(min_detection_confidence=0.5, min_tracking_confidence=0.5) as pose: | |
| while cap.isOpened(): | |
| ret, frame = cap.read() | |
| if not ret: | |
| st.error("❌ Impossible d'accéder à la webcam.") | |
| break | |
| image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) | |
| image.flags.writeable = False | |
| results = pose.process(image) | |
| image.flags.writeable = True | |
| image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR) | |
| if results.pose_landmarks: | |
| mp_drawing.draw_landmarks(image, results.pose_landmarks, mp_pose.POSE_CONNECTIONS) | |
| lm = results.pose_landmarks.landmark | |
| feedback = "" | |
| # Règles selon exercice choisi | |
| if selected_exercise == "Bras levés": | |
| wrist_l = lm[mp_pose.PoseLandmark.LEFT_WRIST.value] | |
| wrist_r = lm[mp_pose.PoseLandmark.RIGHT_WRIST.value] | |
| nose = lm[mp_pose.PoseLandmark.NOSE.value] | |
| bras_gauche_ok = wrist_l.y < nose.y | |
| bras_droit_ok = wrist_r.y < nose.y | |
| if bras_gauche_ok and bras_droit_ok: | |
| feedback = "✅ Les bras sont bien levés" | |
| else: | |
| if not bras_gauche_ok: | |
| feedback += "💡 Bras gauche trop bas !\n" | |
| if not bras_droit_ok: | |
| feedback += "💡 Bras droit trop bas !" | |
| elif selected_exercise == "Squat": | |
| knee_l = lm[mp_pose.PoseLandmark.LEFT_KNEE.value] | |
| ankle_l = lm[mp_pose.PoseLandmark.LEFT_ANKLE.value] | |
| hip_l = lm[mp_pose.PoseLandmark.LEFT_HIP.value] | |
| if hip_l.y > knee_l.y and knee_l.y < ankle_l.y + 0.05: | |
| feedback = "✅ Bonne position de squat" | |
| else: | |
| feedback = "💡 Descendez vos hanches en gardant le dos droit" | |
| elif selected_exercise == "Inclinaison latérale": | |
| shoulder_l = lm[mp_pose.PoseLandmark.LEFT_SHOULDER.value] | |
| shoulder_r = lm[mp_pose.PoseLandmark.RIGHT_SHOULDER.value] | |
| delta_x = abs(shoulder_l.x - shoulder_r.x) | |
| if delta_x > 0.2: | |
| feedback = "✅ Bonne inclinaison latérale" | |
| else: | |
| feedback = "💡 Inclinez davantage le buste sur le côté" | |
| # Affichage texte | |
| feedback_placeholder.markdown(f"### 🎤 Feedback :\n{feedback}") | |
| # Retour vocal asynchrone si message changé | |
| current_time = time.time() | |
| if feedback != previous_feedback and current_time - last_speech_time > MIN_DELAY: | |
| speak_async(feedback.replace("\n", " ")) | |
| previous_feedback = feedback | |
| last_speech_time = current_time | |
| stframe.image(image, channels="BGR", use_column_width=True) | |
| cap.release() | |