Prajithr04 commited on
Commit
c231584
·
1 Parent(s): d2c645a
Files changed (1) hide show
  1. app.py +72 -23
app.py CHANGED
@@ -28,7 +28,8 @@ pose = mp_pose.Pose(
28
  min_detection_confidence=0.5,
29
  min_tracking_confidence=0.5
30
  )
31
- # Function to calculate angles between points
 
32
  def calculate_angle(a, b, c):
33
  ab = (b[0] - a[0], b[1] - a[1])
34
  bc = (c[0] - b[0], c[1] - b[1])
@@ -37,12 +38,59 @@ def calculate_angle(a, b, c):
37
  magnitude_ab = math.sqrt(ab[0]**2 + ab[1]**2)
38
  magnitude_bc = math.sqrt(bc[0]**2 + bc[1]**2)
39
 
40
- angle_radians = math.acos(dot_product / (magnitude_ab * magnitude_bc))
 
 
 
 
 
 
41
  angle_degrees = math.degrees(angle_radians)
42
 
43
  return angle_degrees
44
 
45
- # Process image with Mediapipe Pose Estimation
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
46
  def process_frame(image):
47
  h, w, _ = image.shape
48
 
@@ -56,59 +104,60 @@ def process_frame(image):
56
  image = cv2.cvtColor(image_rgb, cv2.COLOR_RGB2BGR)
57
 
58
  if results.pose_landmarks:
59
- # Get landmarks
60
  right_shoulder = results.pose_landmarks.landmark[mp_pose.PoseLandmark.RIGHT_SHOULDER]
61
  right_hip = results.pose_landmarks.landmark[mp_pose.PoseLandmark.RIGHT_HIP]
62
  right_ear = results.pose_landmarks.landmark[mp_pose.PoseLandmark.RIGHT_EAR]
63
 
64
- # Convert to pixel coordinates
65
  cx_rs, cy_rs = int(right_shoulder.x * w), int(right_shoulder.y * h)
66
  cx_rh, cy_rh = int(right_hip.x * w), int(right_hip.y * h)
67
  cx_re, cy_re = int(right_ear.x * w), int(right_ear.y * h)
68
 
69
- # Create upper reference points
70
  offset = 60
71
  upper_shoulder = (cx_rs, max(0, cy_rs - offset))
72
  upper_hip = (cx_rh, max(0, cy_rh - offset))
73
 
74
- # Draw landmarks
75
  cv2.circle(image, upper_shoulder, 5, (0, 255, 0), -1)
76
  cv2.circle(image, upper_hip, 5, (0, 255, 0), -1)
77
 
78
- # Draw lines
79
  cv2.line(image, (cx_rh, cy_rh), (cx_rs, cy_rs), (255, 0, 255), 2) # Hip to shoulder
80
  cv2.line(image, (cx_rs, cy_rs), (cx_re, cy_re), (255, 255, 0), 2) # Shoulder to ear
81
  cv2.line(image, (cx_rh, cy_rh), upper_hip, (0, 165, 255), 2) # Hip to upper hip
82
  cv2.line(image, (cx_rs, cy_rs), upper_shoulder, (0, 255, 255), 2) # Shoulder to upper shoulder
83
 
84
- # Calculate angles
85
  angle_hip = calculate_angle(upper_hip, (cx_rh, cy_rh), (cx_rs, cy_rs))
86
  angle_neck = calculate_angle((cx_rs, cy_rs), (cx_re, cy_re), upper_shoulder)
87
 
88
- # Determine posture status
89
- hip_posture = "Good" if 160 <= angle_hip <= 180 else "Poor"
90
- neck_posture = "Good" if 150 <= angle_neck <= 180 else "Poor"
91
- hip_color = (0, 255, 0) if hip_posture == "Good" else (0, 0, 255)
92
- neck_color = (0, 255, 0) if neck_posture == "Good" else (0, 0, 255)
 
 
 
93
 
94
- # Display angles
95
- cv2.putText(image, f"Hip Angle: {angle_hip:.1f} ({hip_posture})", (10, 60),
96
- cv2.FONT_HERSHEY_SIMPLEX, 0.6, hip_color, 2)
97
- cv2.putText(image, f"Neck Angle: {angle_neck:.1f} ({neck_posture})", (10, 90),
98
- cv2.FONT_HERSHEY_SIMPLEX, 0.6, neck_color, 2)
99
 
100
  return image
101
 
102
- # API Route to receive an image and return processed image
103
  @app.post("/upload")
104
  async def upload_image(file: UploadFile = File(...)):
105
  contents = await file.read()
106
  image = Image.open(BytesIO(contents))
107
  image = cv2.cvtColor(np.array(image), cv2.COLOR_RGB2BGR)
108
 
109
- # Process the image in async context
110
  processed_image = process_frame(image)
111
 
112
- # Encode processed image to return
113
  _, buffer = cv2.imencode(".jpg", processed_image)
114
- return Response(content=buffer.tobytes(), media_type="image/jpeg")
 
28
  min_detection_confidence=0.5,
29
  min_tracking_confidence=0.5
30
  )
31
+
32
+ # Function to calculate angles between three points
33
  def calculate_angle(a, b, c):
34
  ab = (b[0] - a[0], b[1] - a[1])
35
  bc = (c[0] - b[0], c[1] - b[1])
 
38
  magnitude_ab = math.sqrt(ab[0]**2 + ab[1]**2)
39
  magnitude_bc = math.sqrt(bc[0]**2 + bc[1]**2)
40
 
41
+ # To avoid division by zero
42
+ if magnitude_ab * magnitude_bc == 0:
43
+ return 0.0
44
+
45
+ # Clamp the cosine value to the [-1, 1] range to avoid numerical errors
46
+ cosine_angle = max(min(dot_product / (magnitude_ab * magnitude_bc), 1), -1)
47
+ angle_radians = math.acos(cosine_angle)
48
  angle_degrees = math.degrees(angle_radians)
49
 
50
  return angle_degrees
51
 
52
+ # Function to calculate a simplified REBA score based on trunk (hip) and neck angles.
53
+ def calculate_reba(trunk_angle, neck_angle):
54
+ """
55
+ This is a simplified approach:
56
+ - For the trunk (approximated by the hip angle), a nearly upright posture (angle >= 160°) is scored as 1,
57
+ a moderately bent posture (angle between 140° and 160°) is scored as 2, and a severely bent posture (<140°) is scored as 3.
58
+ - Similarly for the neck angle.
59
+ - The REBA score is the sum of these scores.
60
+ - Finally, we define a risk level based on the total score.
61
+ """
62
+ # Determine trunk score (using the hip angle)
63
+ if trunk_angle >= 160:
64
+ trunk_score = 1
65
+ elif trunk_angle >= 140:
66
+ trunk_score = 2
67
+ else:
68
+ trunk_score = 3
69
+
70
+ # Determine neck score
71
+ if neck_angle >= 150:
72
+ neck_score = 1
73
+ elif neck_angle >= 130:
74
+ neck_score = 2
75
+ else:
76
+ neck_score = 3
77
+
78
+ # Simplified REBA group A score (normally REBA also considers legs, arms, load, etc.)
79
+ reba_score = trunk_score + neck_score
80
+
81
+ # Define risk levels based on the score
82
+ if reba_score <= 2:
83
+ risk = "Negligible"
84
+ elif reba_score <= 4:
85
+ risk = "Low"
86
+ elif reba_score <= 6:
87
+ risk = "Medium"
88
+ else:
89
+ risk = "High"
90
+
91
+ return reba_score, risk
92
+
93
+ # Process image with Mediapipe Pose Estimation and analyze posture using REBA score
94
  def process_frame(image):
95
  h, w, _ = image.shape
96
 
 
104
  image = cv2.cvtColor(image_rgb, cv2.COLOR_RGB2BGR)
105
 
106
  if results.pose_landmarks:
107
+ # Get key landmarks from the right side
108
  right_shoulder = results.pose_landmarks.landmark[mp_pose.PoseLandmark.RIGHT_SHOULDER]
109
  right_hip = results.pose_landmarks.landmark[mp_pose.PoseLandmark.RIGHT_HIP]
110
  right_ear = results.pose_landmarks.landmark[mp_pose.PoseLandmark.RIGHT_EAR]
111
 
112
+ # Convert normalized coordinates to pixel coordinates
113
  cx_rs, cy_rs = int(right_shoulder.x * w), int(right_shoulder.y * h)
114
  cx_rh, cy_rh = int(right_hip.x * w), int(right_hip.y * h)
115
  cx_re, cy_re = int(right_ear.x * w), int(right_ear.y * h)
116
 
117
+ # Create reference points by applying an offset (helps approximate vertical)
118
  offset = 60
119
  upper_shoulder = (cx_rs, max(0, cy_rs - offset))
120
  upper_hip = (cx_rh, max(0, cy_rh - offset))
121
 
122
+ # Draw reference landmarks on the image
123
  cv2.circle(image, upper_shoulder, 5, (0, 255, 0), -1)
124
  cv2.circle(image, upper_hip, 5, (0, 255, 0), -1)
125
 
126
+ # Draw lines connecting key points
127
  cv2.line(image, (cx_rh, cy_rh), (cx_rs, cy_rs), (255, 0, 255), 2) # Hip to shoulder
128
  cv2.line(image, (cx_rs, cy_rs), (cx_re, cy_re), (255, 255, 0), 2) # Shoulder to ear
129
  cv2.line(image, (cx_rh, cy_rh), upper_hip, (0, 165, 255), 2) # Hip to upper hip
130
  cv2.line(image, (cx_rs, cy_rs), upper_shoulder, (0, 255, 255), 2) # Shoulder to upper shoulder
131
 
132
+ # Calculate angles using the defined reference points
133
  angle_hip = calculate_angle(upper_hip, (cx_rh, cy_rh), (cx_rs, cy_rs))
134
  angle_neck = calculate_angle((cx_rs, cy_rs), (cx_re, cy_re), upper_shoulder)
135
 
136
+ # Compute the simplified REBA score and corresponding risk level
137
+ reba_score, risk = calculate_reba(angle_hip, angle_neck)
138
+
139
+ # Display the calculated angles on the image
140
+ cv2.putText(image, f"Hip Angle: {angle_hip:.1f}", (10, 60),
141
+ cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 0, 255), 2)
142
+ cv2.putText(image, f"Neck Angle: {angle_neck:.1f}", (10, 90),
143
+ cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 255, 0), 2)
144
 
145
+ # Display the simplified REBA score and risk level on the image
146
+ cv2.putText(image, f"REBA Score: {reba_score} ({risk})", (10, 120),
147
+ cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 255), 2)
 
 
148
 
149
  return image
150
 
151
+ # API Route to receive an image and return the processed image with REBA analysis
152
  @app.post("/upload")
153
  async def upload_image(file: UploadFile = File(...)):
154
  contents = await file.read()
155
  image = Image.open(BytesIO(contents))
156
  image = cv2.cvtColor(np.array(image), cv2.COLOR_RGB2BGR)
157
 
158
+ # Process the image (the processing function now includes REBA score analysis)
159
  processed_image = process_frame(image)
160
 
161
+ # Encode the processed image to return it as JPEG
162
  _, buffer = cv2.imencode(".jpg", processed_image)
163
+ return Response(content=buffer.tobytes(), media_type="image/jpeg")