99from utils .plots import output_to_keypoint
1010
1111
12+ def compute_center_of_mass (pose ):
13+ left_shoulder = (pose [10 ], pose [11 ])
14+ right_shoulder = (pose [13 ], pose [14 ])
15+ left_hip = (pose [22 ], pose [23 ])
16+ right_hip = (pose [25 ], pose [26 ])
17+ cx = (left_shoulder [0 ] + right_shoulder [0 ] + left_hip [0 ] + right_hip [0 ]) / 4
18+ cy = (left_shoulder [1 ] + right_shoulder [1 ] + left_hip [1 ] + right_hip [1 ]) / 4
19+ return cx , cy
20+
21+
22+ def compute_center_velocity (pose_start , pose_end , fps , window_size ):
23+ com_start = compute_center_of_mass (pose_start )
24+ com_end = compute_center_of_mass (pose_end )
25+ dx = com_end [0 ] - com_start [0 ]
26+ dy = com_end [1 ] - com_start [1 ]
27+ distance = math .sqrt (dx ** 2 + dy ** 2 )
28+ time_elapsed = (window_size - 1 ) / fps
29+ velocity = distance / time_elapsed
30+ return min (velocity , 300.0 ), dy
31+
32+
33+ def compute_bbox_aspect_ratio (pose ):
34+ x_vals = [pose [i ] for i in range (0 , len (pose ), 3 )]
35+ y_vals = [pose [i ] for i in range (1 , len (pose ), 3 )]
36+ width = max (x_vals ) - min (x_vals )
37+ height = max (y_vals ) - min (y_vals )
38+ return width / height if height != 0 else 0
39+
40+
41+ def compute_aspect_ratio_delta (pose_start , pose_end ):
42+ ar_start = compute_bbox_aspect_ratio (pose_start )
43+ ar_end = compute_bbox_aspect_ratio (pose_end )
44+ return ar_end - ar_start , ar_start , ar_end
45+
46+
47+ def find_most_similar_pose (reference_pose , candidate_poses ):
48+ ref_cx , ref_cy = compute_center_of_mass (reference_pose )
49+ min_dist = float ("inf" )
50+ best_pose = None
51+ for pose in candidate_poses :
52+ cx , cy = compute_center_of_mass (pose )
53+ dist = (ref_cx - cx )** 2 + (ref_cy - cy )** 2
54+ if dist < min_dist :
55+ min_dist = dist
56+ best_pose = pose
57+ return best_pose
58+
59+
1260def get_pose_model ():
1361 device = torch .device ("cuda:0" if torch .cuda .is_available () else "cpu" )
1462 print ("device: " , device )
@@ -49,7 +97,6 @@ def prepare_vid_out(video_path, vid_cap, output_dir):
4997 if not success :
5098 raise RuntimeError (f"Failed to read first frame for output setup: { video_path } " )
5199
52- # Reset video capture position to beginning
53100 vid_cap .set (cv2 .CAP_PROP_POS_FRAMES , 0 )
54101
55102 vid_write_image = letterbox (first_frame , 960 , stride = 64 , auto = True )[0 ]
@@ -67,37 +114,40 @@ def prepare_vid_out(video_path, vid_cap, output_dir):
67114 return out
68115
69116
70- def fall_detection (poses ):
71- for pose in poses :
72- xmin , ymin = (pose [2 ] - pose [4 ] / 2 ), (pose [3 ] - pose [5 ] / 2 )
73- xmax , ymax = (pose [2 ] + pose [4 ] / 2 ), (pose [3 ] + pose [5 ] / 2 )
74- left_shoulder_y = pose [23 ]
75- left_shoulder_x = pose [22 ]
76- right_shoulder_y = pose [26 ]
77- left_body_y = pose [41 ]
78- left_body_x = pose [40 ]
79- right_body_y = pose [44 ]
80- len_factor = math .sqrt (
81- (left_shoulder_y - left_body_y ) ** 2 + (left_shoulder_x - left_body_x ) ** 2
82- )
83- left_foot_y = pose [53 ]
84- right_foot_y = pose [56 ]
85- dx = int (xmax ) - int (xmin )
86- dy = int (ymax ) - int (ymin )
87- difference = dy - dx
88- if (
89- left_shoulder_y > left_foot_y - len_factor
90- and left_body_y > left_foot_y - (len_factor / 2 )
91- and left_shoulder_y > left_body_y - (len_factor / 2 )
92- or (
93- right_shoulder_y > right_foot_y - len_factor
94- and right_body_y > right_foot_y - (len_factor / 2 )
95- and right_shoulder_y > right_body_y - (len_factor / 2 )
96- )
97- or difference < 0
98- ):
99- return True , (xmin , ymin , xmax , ymax )
100- return False , None
117+ def fall_detection (pose_window , window_size , fps , v_thresh , aspect_ratio_thresh , dy_thresh ):
118+ if len (pose_window ) < window_size :
119+ return False , None , None , None
120+
121+ pose_start = pose_window [0 ][0 ]
122+ pose_end_all = pose_window [- 1 ]
123+ pose_end = find_most_similar_pose (pose_start , pose_end_all )
124+
125+ v , dy = compute_center_velocity (pose_start , pose_end , fps , window_size )
126+ ar_delta , ar_start , ar_end = compute_aspect_ratio_delta (pose_start , pose_end )
127+
128+ debug_text = (
129+ f"v={ v :.2f} /{ v_thresh :.2f} px/s, "
130+ f"y={ dy :.1f} /{ dy_thresh :.1f} , "
131+ f"ar={ ar_delta :.2f} /{ aspect_ratio_thresh :.2f} "
132+ )
133+ print (f"[TRACE] { debug_text } " )
134+
135+ cond_speed_drop = v > v_thresh and dy > dy_thresh
136+ cond_down_flat = dy > dy_thresh and ar_delta > aspect_ratio_thresh
137+
138+ if cond_speed_drop or cond_down_flat :
139+ tag = (
140+ ("SpeedDrop " if cond_speed_drop else "" ) +
141+ ("DownFlat " if cond_down_flat else "" )
142+ ).strip ()
143+
144+ xmin = pose_end [2 ] - pose_end [4 ] / 2
145+ ymin = pose_end [3 ] - pose_end [5 ] / 2
146+ xmax = pose_end [2 ] + pose_end [4 ] / 2
147+ ymax = pose_end [3 ] + pose_end [5 ] / 2
148+ return True , (xmin , ymin , xmax , ymax ), debug_text , tag
149+
150+ return False , None , debug_text , ""
101151
102152
103153def falling_alarm (image , bbox ):
@@ -121,7 +171,6 @@ def falling_alarm(image, bbox):
121171 lineType = cv2 .LINE_AA ,
122172 )
123173
124-
125174def draw_fps (frame , prev_time ):
126175 import time
127176 curr_time = time .time ()
0 commit comments