fall_cls.py 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. import torch
  2. import torch.nn as nn
  3. import torch.nn.functional as F
  4. from pydantic import BaseModel
  5. from ultralytics import YOLO
  6. import os
  7. import cv2
  8. import numpy as np
  9. class Model(nn.Module):
  10. def __init__(self, A, nnode, nfeature, nclass):
  11. super().__init__()
  12. self.fc1 = nn.Linear(nnode * nfeature, 512)
  13. self.fc2 = nn.Linear(512, nclass)
  14. def forward(self, x):
  15. x = x.view(-1, int(x.size(1) * x.size(2)))
  16. x = F.relu(self.fc1(x))
  17. x = F.dropout(x, 0.7, training=self.training)
  18. return self.fc2(x)
  19. def extract_keypoint(get_keypoint, keypoint):
  20. # nose
  21. nose_x, nose_y = keypoint[get_keypoint.NOSE]
  22. # eye
  23. # left_eye_x, left_eye_y = keypoint[get_keypoint.LEFT_EYE]
  24. # right_eye_x, right_eye_y = keypoint[get_keypoint.RIGHT_EYE]
  25. # # ear
  26. # left_ear_x, left_ear_y = keypoint[get_keypoint.LEFT_EAR]
  27. # right_ear_x, right_ear_y = keypoint[get_keypoint.RIGHT_EAR]
  28. # shoulder
  29. left_shoulder_x, left_shoulder_y = keypoint[get_keypoint.LEFT_SHOULDER]
  30. right_shoulder_x, right_shoulder_y = keypoint[get_keypoint.RIGHT_SHOULDER]
  31. # elbow
  32. left_elbow_x, left_elbow_y = keypoint[get_keypoint.LEFT_ELBOW]
  33. right_elbow_x, right_elbow_y = keypoint[get_keypoint.RIGHT_ELBOW]
  34. # wrist
  35. left_wrist_x, left_wrist_y = keypoint[get_keypoint.LEFT_WRIST]
  36. right_wrist_x, right_wrist_y = keypoint[get_keypoint.RIGHT_WRIST]
  37. # hip
  38. left_hip_x, left_hip_y = keypoint[get_keypoint.LEFT_HIP]
  39. right_hip_x, right_hip_y = keypoint[get_keypoint.RIGHT_HIP]
  40. # knee
  41. left_knee_x, left_knee_y = keypoint[get_keypoint.LEFT_KNEE]
  42. right_knee_x, right_knee_y = keypoint[get_keypoint.RIGHT_KNEE]
  43. # ankle
  44. left_ankle_x, left_ankle_y = keypoint[get_keypoint.LEFT_ANKLE]
  45. right_ankle_x, right_ankle_y = keypoint[get_keypoint.RIGHT_ANKLE]
  46. return [
  47. nose_x, nose_y ,
  48. left_shoulder_x, left_shoulder_y ,
  49. right_shoulder_x, right_shoulder_y,
  50. left_elbow_x, left_elbow_y,
  51. right_elbow_x, right_elbow_y,
  52. left_wrist_x, left_wrist_y,
  53. right_wrist_x, right_wrist_y,
  54. left_hip_x, left_hip_y,
  55. right_hip_x, right_hip_y,
  56. left_knee_x, left_knee_y,
  57. right_knee_x, right_knee_y,
  58. left_ankle_x, left_ankle_y,
  59. right_ankle_x, right_ankle_y
  60. ]
  61. class GetKeypoint(BaseModel):
  62. NOSE: int = 0
  63. LEFT_EYE: int = 1
  64. RIGHT_EYE: int = 2
  65. LEFT_EAR: int = 3
  66. RIGHT_EAR: int = 4
  67. LEFT_SHOULDER: int = 5
  68. RIGHT_SHOULDER: int = 6
  69. LEFT_ELBOW: int = 7
  70. RIGHT_ELBOW: int = 8
  71. LEFT_WRIST: int = 9
  72. RIGHT_WRIST: int = 10
  73. LEFT_HIP: int = 11
  74. RIGHT_HIP: int = 12
  75. LEFT_KNEE: int = 13
  76. RIGHT_KNEE: int = 14
  77. LEFT_ANKLE: int = 15
  78. RIGHT_ANKLE: int = 16
  79. class Box(BaseModel):
  80. left : int
  81. top : int
  82. right : int
  83. bottom : int
  84. box_conf : float
  85. pose_classifer_conf : float
  86. label : str
  87. class YoloPoseClassfier(object):
  88. def __init__(self, device_id, yolo_pose_model_path, pose_classfier_model_path, confidence_threshold=0.5):
  89. self.confidence_threshold = confidence_threshold
  90. self.device = torch.device("cpu") if device_id == -1 else torch.device(f"cuda:{device_id}")
  91. # define get key point function
  92. self.get_keypoint = GetKeypoint()
  93. # load model
  94. self.pose_classfier_model = Model(None, 13, 2, 2)
  95. self.pose_classfier_model.load_state_dict(torch.load(pose_classfier_model_path))
  96. self.pose_classfier_model.eval()
  97. self.pose_classfier_model.to(self.device)
  98. self.yolo_pose_model = YOLO(yolo_pose_model_path)
  99. self.yolo_pose_model.to(self.device)
  100. # 新增躯干角度校验函数
  101. def calculate_torso_angle(keypoints):
  102. # 获取肩膀(索引5、6)和臀部(索引11、12)关键点
  103. left_shoulder = keypoints[5]
  104. right_shoulder = keypoints[6]
  105. left_hip = keypoints[11]
  106. right_hip = keypoints[12]
  107. # 计算肩膀中点与臀部中点的向量
  108. shoulder_center = ((left_shoulder[0] + right_shoulder[0]) / 2,
  109. (left_shoulder[1] + right_shoulder[1]) / 2)
  110. hip_center = ((left_hip[0] + right_hip[0]) / 2,
  111. (left_hip[1] + right_hip[1]) / 2)
  112. # 计算与垂直方向的夹角(跌倒时角度接近90度)
  113. dx = hip_center[0] - shoulder_center[0]
  114. dy = hip_center[1] - shoulder_center[1]
  115. angle = np.degrees(np.arctan2(abs(dx), abs(dy)))
  116. return angle
  117. def __call__(self, image : np.ndarray):
  118. pose_classfier_results = []
  119. yolo_pose_results = self.yolo_pose_model(image, conf=self.confidence_threshold)
  120. for result in yolo_pose_results[0]:
  121. boxes = result.boxes.xyxy.cpu().numpy().tolist()
  122. confs = result.boxes.conf.cpu().numpy().tolist()
  123. all_keypoints = result.keypoints.data.cpu().numpy().tolist()
  124. for box, conf, keypoints in zip(boxes, confs, all_keypoints):
  125. x1, y1, x2, y2 = box
  126. x, y, w, h = x1, y1, x2 - x1, y2 - y1
  127. n_keypoints = [[(kp[0] - x) / w - 0.5, (kp[1] - y) / h - 0.5] if kp[0] > 0 and kp[1] > 0 else kp[:2] for kp in keypoints]
  128. n_keypoints = extract_keypoint(self.get_keypoint, n_keypoints)
  129. if n_keypoints[-12:].count(0) >= 2 * 2:
  130. continue
  131. if n_keypoints.count(0) >= 4 * 2:
  132. continue
  133. if w < h:
  134. continue
  135. pose_data = torch.Tensor([n_keypoints]).to(self.device)
  136. pose_data = pose_data.reshape(1, 13, 2)
  137. with torch.no_grad():
  138. p = self.pose_classfier_model(pose_data)
  139. prob = F.softmax(p)
  140. index = prob.argmax()
  141. if index == 0:
  142. score = float(prob[0][index].cpu().numpy())
  143. pose_classfier_results.append(
  144. Box(left=x1, top=y1, right=x2, bottom=y2, box_conf=conf, pose_classifer_conf=score, label="falling"))
  145. return pose_classfier_results
  146. if __name__ == "__main__":
  147. yolo_pose_model_path = "yolo11l-pose.pt"
  148. pose_classfier_model_path = "fall_cls.pth"
  149. model = YoloPoseClassfier(-1, yolo_pose_model_path, pose_classfier_model_path, 0.5)
  150. test_images_dir = r"C:\Users\86187\Desktop\fall_test"
  151. save_image_dir = "."
  152. for image_name in os.listdir(test_images_dir):
  153. print(image_name)
  154. image_path = os.path.join(test_images_dir, image_name)
  155. image = cv2.imread(image_path)
  156. pose_classfier_results = model(image)
  157. for res in pose_classfier_results:
  158. cv2.rectangle(image, (int(res.left), int(res.top)), (int(res.right), int(res.bottom)), (0, 255, 0), 2)
  159. cv2.imwrite(os.path.join(save_image_dir, image_name), image)