linsoning/Posture_Evaluation_App
0
1import os2os.environ["MPLCONFIGDIR"] = "/tmp/matplotlib-cache"3import cv24import mediapipe as mp5import gradio as gr6import math7import time8 9 10def find_angle(x1, y1, x2, y2):11 return abs(int(math.degrees(math.atan2(x2 - x1, y1 - y2))))12 13def detect_posture(frame, last_alert_time=0):14 # Initialize MediaPipe Pose with the same stuff15 # Oh and I "copied" this code from the Documentation16 # I moved this dumb stuff here, because it can't save to http://usr/local/17 mp_pose = mp.solutions.pose18 pose = mp_pose.Pose(model_complexity=0, min_detection_confidence=0.2, min_tracking_confidence=0.2, smooth_landmarks=False)19 #Also now I can't put the shortened shortcut outside the function anymore since I had to move MediaPipe in here :(20 LEFT_SHOULDER = mp_pose.PoseLandmark.LEFT_SHOULDER21 LEFT_EAR = mp_pose.PoseLandmark.LEFT_EAR22 LEFT_HIP = mp_pose.PoseLandmark.LEFT_HIP23 24 # This is the start time for my new FPS counter25 start_time = time.perf_counter()26 27 THEALERTPATH = None # What's happening here, is that THEALERTPATH is NOT playing28 29 #Basically here, doing 3 conversions is slow, so by predefining the "results" using only 1 convertion...30 #It would be faster since a need to convert back to BGR is unnecessary31 rgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)32 results = pose.process(rgb_frame)33 #And since we aren't using image anymore...34 #This is what the computer will draw on35 output_frame = frame.copy()36 37 if results.pose_landmarks:38 # Get frame dimensions39 height, width = frame.shape[:2]40 allthelandmarks = results.pose_landmarks.landmark41 42 #This is why I defined it earlier43 shoulder = allthelandmarks[LEFT_SHOULDER]44 ear = allthelandmarks[LEFT_EAR]45 hip = allthelandmarks[LEFT_HIP]46 47 #Pixel coordinates48 shoulder_x, shoulder_y, ear_x, ear_y, hip_x, hip_y = int(shoulder.x * width), int(shoulder.y * height), int(ear.x * width), int(ear.y * height), int(hip.x * width), int(hip.y * height)49 50 # Calculate angles51 neck_angle = find_angle(shoulder_x, shoulder_y, ear_x, ear_y)52 torso_angle = find_angle(hip_x, hip_y, shoulder_x, shoulder_y)53 54 # Posture alert with cooldown55 current_time = time.time()56 57 if neck_angle > 40 or torso_angle > 10:58 if current_time - last_alert_time > 2:59 last_alert_time = current_time60 cv2.putText(output_frame, "BAD POSTURE!", (20, 40),61 cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 0, 255), 2)62 THEALERTPATH = "Noise.mp3" #This plays the sound63 64 #same stuff as always, just with a different name65 cv2.line(output_frame, (shoulder_x, shoulder_y), (ear_x, ear_y), (0, 255, 0), 2)66 cv2.line(output_frame, (hip_x, hip_y), (shoulder_x, shoulder_y), (255, 0, 0), 2)67 cv2.line(output_frame, (shoulder_x, shoulder_y), (shoulder_x, shoulder_y - 100), (200, 200, 200), 1)68 cv2.line(output_frame, (hip_x, hip_y), (hip_x, hip_y - 100), (200, 200, 200), 1)69 70 # The fps counter code (I'll explain this in logbook)71 process_time = time.perf_counter() - start_time72 fps = 1 / process_time if process_time > 0 else 073 cv2.putText(output_frame, f"FPS: {fps:.1f}", (width-120, 40),74 cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 255), 2)75 76 return cv2.cvtColor(output_frame, cv2.COLOR_BGR2RGB), last_alert_time, THEALERTPATH #This will now return the MP3 File on top of everything else77 78# It sends the incoming frame + the state (last_alert_time) to detect_posture(), and returns the updated frame + updated state.79# This acts like a bridge between Gradio's webcam stream and their posture detection function80def process_frame(input_frame, state=0):81 output_frame, new_state, alert_audio = detect_posture(input_frame, state)82 return output_frame, new_state, alert_audio83 84 85with gr.Blocks(title="V3 OF POSTURE DETECTION") as app: #This will create the Gradio Blocks interface86 gr.Markdown("If you don't straighten that back of yours, this program will shout at you!")87 with gr.Row(): # This will make the next 2 things side by side.88 #This is the webcam, obviously89 webcam = gr.Webcam(label="You")90 #The output with all the landmarks, and non-customization91 output = gr.Image(label="You but with landmarks", interactive=False)92 93 #Extra option for SOUND94 #This creates an audio player in Gradio that plays NOISE.mp395 alert_audio = gr.Audio(label="Audio Alert", autoplay=True)96 97 #This stores the last alert time98 state = gr.State(0)99 100 webcam.stream(101 # Sends webcam frames and last alert time (state) to process_frame102 # The updated image, state, and an MP3 path (if bad posture) are returned103 # Shows the image, state, and plays the sound automatically104 process_frame,105 inputs=[webcam, state],106 outputs=[output, state, alert_audio], # Plays audio only if a file is returned107 show_progress="hidden",108 )109 110if __name__ == "__main__":111 app.launch()