human_posture_analysis_video.py 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. import cv2
  2. import time
  3. import math as m
  4. import mediapipe as mp
  5. # Calculate distance
  6. def findDistance(x1, y1, x2, y2):
  7. dist = m.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2)
  8. return dist
  9. # Calculate angle.
  10. def findAngle(x1, y1, x2, y2):
  11. theta = m.acos((y2 - y1) * (-y1) / (m.sqrt(
  12. (x2 - x1) ** 2 + (y2 - y1) ** 2) * y1))
  13. degree = int(180 / m.pi) * theta
  14. return degree
  15. """
  16. Function to send alert. Use this function to send alert when bad posture detected.
  17. Feel free to get creative and customize as per your convenience.
  18. """
  19. def sendWarning(x):
  20. pass
  21. # =============================CONSTANTS and INITIALIZATIONS=====================================#
  22. # Initilize frame counters.
  23. good_frames = 0
  24. bad_frames = 0
  25. # Font type.
  26. font = cv2.FONT_HERSHEY_SIMPLEX
  27. # Colors.
  28. blue = (255, 127, 0)
  29. red = (50, 50, 255)
  30. green = (127, 255, 0)
  31. dark_blue = (127, 20, 0)
  32. light_green = (127, 233, 100)
  33. yellow = (0, 255, 255)
  34. pink = (255, 0, 255)
  35. # Initialize mediapipe pose class.
  36. mp_pose = mp.solutions.pose
  37. pose = mp_pose.Pose()
  38. # ===============================================================================================#
  39. if __name__ == "__main__":
  40. # For webcam input replace file name with 0.
  41. file_name = 'input.mp4'
  42. cap = cv2.VideoCapture(file_name)
  43. # Meta.
  44. fps = int(cap.get(cv2.CAP_PROP_FPS))
  45. width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
  46. height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
  47. frame_size = (width, height)
  48. fourcc = cv2.VideoWriter_fourcc(*'mp4v')
  49. # Video writer.
  50. video_output = cv2.VideoWriter('output.mp4', fourcc, fps, frame_size)
  51. while cap.isOpened():
  52. # Capture frames.
  53. success, image = cap.read()
  54. if not success:
  55. print("Null.Frames")
  56. break
  57. # Get fps.
  58. fps = cap.get(cv2.CAP_PROP_FPS)
  59. # Get height and width.
  60. h, w = image.shape[:2]
  61. # Convert the BGR image to RGB.
  62. image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
  63. # Process the image.
  64. keypoints = pose.process(image)
  65. # Convert the image back to BGR.
  66. image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)
  67. # Use lm and lmPose as representative of the following methods.
  68. lm = keypoints.pose_landmarks
  69. lmPose = mp_pose.PoseLandmark
  70. # Acquire the landmark coordinates.
  71. # Once aligned properly, left or right should not be a concern.
  72. # Left shoulder.
  73. l_shldr_x = int(lm.landmark[lmPose.LEFT_SHOULDER].x * w)
  74. l_shldr_y = int(lm.landmark[lmPose.LEFT_SHOULDER].y * h)
  75. # Right shoulder
  76. r_shldr_x = int(lm.landmark[lmPose.RIGHT_SHOULDER].x * w)
  77. r_shldr_y = int(lm.landmark[lmPose.RIGHT_SHOULDER].y * h)
  78. # Left ear.
  79. l_ear_x = int(lm.landmark[lmPose.LEFT_EAR].x * w)
  80. l_ear_y = int(lm.landmark[lmPose.LEFT_EAR].y * h)
  81. # Left hip.
  82. l_hip_x = int(lm.landmark[lmPose.LEFT_HIP].x * w)
  83. l_hip_y = int(lm.landmark[lmPose.LEFT_HIP].y * h)
  84. # Calculate distance between left shoulder and right shoulder points.
  85. offset = findDistance(l_shldr_x, l_shldr_y, r_shldr_x, r_shldr_y)
  86. # Assist to align the camera to point at the side view of the person.
  87. # Offset threshold 30 is based on results obtained from analysis over 100 samples.
  88. if offset < 100:
  89. cv2.putText(image, str(int(offset)) + ' Aligned', (w - 150, 30), font, 0.9, green, 2)
  90. else:
  91. cv2.putText(image, str(int(offset)) + ' Not Aligned', (w - 150, 30), font, 0.9, red, 2)
  92. # Calculate angles.
  93. neck_inclination = findAngle(l_shldr_x, l_shldr_y, l_ear_x, l_ear_y)
  94. torso_inclination = findAngle(l_hip_x, l_hip_y, l_shldr_x, l_shldr_y)
  95. # Draw landmarks.
  96. cv2.circle(image, (l_shldr_x, l_shldr_y), 7, yellow, -1)
  97. cv2.circle(image, (l_ear_x, l_ear_y), 7, yellow, -1)
  98. # Let's take y - coordinate of P3 100px above x1, for display elegance.
  99. # Although we are taking y = 0 while calculating angle between P1,P2,P3.
  100. cv2.circle(image, (l_shldr_x, l_shldr_y - 100), 7, yellow, -1)
  101. cv2.circle(image, (r_shldr_x, r_shldr_y), 7, pink, -1)
  102. cv2.circle(image, (l_hip_x, l_hip_y), 7, yellow, -1)
  103. # Similarly, here we are taking y - coordinate 100px above x1. Note that
  104. # you can take any value for y, not necessarily 100 or 200 pixels.
  105. cv2.circle(image, (l_hip_x, l_hip_y - 100), 7, yellow, -1)
  106. # Put text, Posture and angle inclination.
  107. # Text string for display.
  108. angle_text_string = 'Neck : ' + str(int(neck_inclination)) + ' Torso : ' + str(int(torso_inclination))
  109. # Determine whether good posture or bad posture.
  110. # The threshold angles have been set based on intuition.
  111. if neck_inclination < 40 and torso_inclination < 10:
  112. bad_frames = 0
  113. good_frames += 1
  114. cv2.putText(image, angle_text_string, (10, 30), font, 0.9, light_green, 2)
  115. cv2.putText(image, str(int(neck_inclination)), (l_shldr_x + 10, l_shldr_y), font, 0.9, light_green, 2)
  116. cv2.putText(image, str(int(torso_inclination)), (l_hip_x + 10, l_hip_y), font, 0.9, light_green, 2)
  117. # Join landmarks.
  118. cv2.line(image, (l_shldr_x, l_shldr_y), (l_ear_x, l_ear_y), green, 4)
  119. cv2.line(image, (l_shldr_x, l_shldr_y), (l_shldr_x, l_shldr_y - 100), green, 4)
  120. cv2.line(image, (l_hip_x, l_hip_y), (l_shldr_x, l_shldr_y), green, 4)
  121. cv2.line(image, (l_hip_x, l_hip_y), (l_hip_x, l_hip_y - 100), green, 4)
  122. else:
  123. good_frames = 0
  124. bad_frames += 1
  125. cv2.putText(image, angle_text_string, (10, 30), font, 0.9, red, 2)
  126. cv2.putText(image, str(int(neck_inclination)), (l_shldr_x + 10, l_shldr_y), font, 0.9, red, 2)
  127. cv2.putText(image, str(int(torso_inclination)), (l_hip_x + 10, l_hip_y), font, 0.9, red, 2)
  128. # Join landmarks.
  129. cv2.line(image, (l_shldr_x, l_shldr_y), (l_ear_x, l_ear_y), red, 4)
  130. cv2.line(image, (l_shldr_x, l_shldr_y), (l_shldr_x, l_shldr_y - 100), red, 4)
  131. cv2.line(image, (l_hip_x, l_hip_y), (l_shldr_x, l_shldr_y), red, 4)
  132. cv2.line(image, (l_hip_x, l_hip_y), (l_hip_x, l_hip_y - 100), red, 4)
  133. # Calculate the time of remaining in a particular posture.
  134. good_time = (1 / fps) * good_frames
  135. bad_time = (1 / fps) * bad_frames
  136. # Pose time.
  137. if good_time > 0:
  138. time_string_good = 'Good Posture Time : ' + str(round(good_time, 1)) + 's'
  139. cv2.putText(image, time_string_good, (10, h - 20), font, 0.9, green, 2)
  140. else:
  141. time_string_bad = 'Bad Posture Time : ' + str(round(bad_time, 1)) + 's'
  142. cv2.putText(image, time_string_bad, (10, h - 20), font, 0.9, red, 2)
  143. # If you stay in bad posture for more than 3 minutes (180s) send an alert.
  144. if bad_time > 180:
  145. sendWarning()
  146. # Write frames.
  147. video_output.write(image)
  148. # Display.
  149. cv2.imshow('MediaPipe Pose', image)
  150. if cv2.waitKey(5) & 0xFF == ord('q'):
  151. break
  152. cap.release()
  153. cv2.destroyAllWindows()