Skip to content

Commit 3200e61

Browse files
authored
Merge pull request #13 from Y-B-Class-Projects/add-ci-test
Add ci test
2 parents 956aed9 + 64d95e9 commit 3200e61

8 files changed

Lines changed: 93 additions & 23 deletions

File tree

.github/workflows/ci.yaml

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
name: Code Quality and Tests
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
- '**'
8+
pull_request:
9+
branches:
10+
- main
11+
12+
jobs:
13+
pr_checks:
14+
runs-on: ubuntu-latest
15+
16+
steps:
17+
- name: Checkout code
18+
uses: actions/checkout@v2
19+
20+
- name: Set up Python
21+
uses: actions/setup-python@v2
22+
with:
23+
python-version: '3.12'
24+
25+
- name: Install dependencies
26+
run: |
27+
pip install -r requirements.txt
28+
29+
- name: Download YOLOv7 pose model
30+
run: |
31+
curl -L -o yolov7-w6-pose.pt https://github.com/WongKinYiu/yolov7/releases/download/v0.1/yolov7-w6-pose.pt
32+
33+
- name: Check Python script for errors
34+
run: |
35+
CI_MODE=1 python video.py
36+
37+
- name: Run Black to check code style
38+
run: |
39+
black --check .

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ data/images/*
2828
!data/images/zidane.jpg
2929
!data/images/bus.jpg
3030
!data/*.sh
31+
!fall_dataset/ci_videos/**
32+
!.github/workflows/ci.yaml
3133

3234
results*.csv
3335

config.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,15 @@
44

55
load_dotenv()
66

7+
78
def get_env_int(var_name, default):
89
try:
910
return int(os.getenv(var_name, default))
1011
except ValueError:
1112
print(f"Warning: {var_name} is not a valid int, using default {default}")
1213
return default
1314

15+
1416
def get_env_float(var_name, default):
1517
try:
1618
return float(os.getenv(var_name, default))

fall_core.py

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ def compute_center_velocity(pose_start, pose_end, fps, window_size):
2424
com_end = compute_center_of_mass(pose_end)
2525
dx = com_end[0] - com_start[0]
2626
dy = com_end[1] - com_start[1]
27-
distance = math.sqrt(dx ** 2 + dy ** 2)
27+
distance = math.sqrt(dx**2 + dy**2)
2828
time_elapsed = (window_size - 1) / fps
2929
velocity = distance / time_elapsed
3030
return min(velocity, 300.0), dy
@@ -50,7 +50,7 @@ def find_most_similar_pose(reference_pose, candidate_poses):
5050
best_pose = None
5151
for pose in candidate_poses:
5252
cx, cy = compute_center_of_mass(pose)
53-
dist = (ref_cx - cx)**2 + (ref_cy - cy)**2
53+
dist = (ref_cx - cx) ** 2 + (ref_cy - cy) ** 2
5454
if dist < min_dist:
5555
min_dist = dist
5656
best_pose = pose
@@ -101,7 +101,7 @@ def prepare_vid_out(video_path, vid_cap, output_dir):
101101

102102
vid_write_image = letterbox(first_frame, 960, stride=64, auto=True)[0]
103103
resize_height, resize_width = vid_write_image.shape[:2]
104-
video_name = os.path.basename(video_path).split('.')[0] + "_output.mp4"
104+
video_name = os.path.basename(video_path).split(".")[0] + "_output.mp4"
105105
out_video_name = os.path.join(output_dir, video_name)
106106
print(f"[INFO] Writing output to: {out_video_name}")
107107

@@ -114,7 +114,9 @@ def prepare_vid_out(video_path, vid_cap, output_dir):
114114
return out
115115

116116

117-
def fall_detection(pose_window, window_size, fps, v_thresh, aspect_ratio_thresh, dy_thresh):
117+
def fall_detection(
118+
pose_window, window_size, fps, v_thresh, aspect_ratio_thresh, dy_thresh
119+
):
118120
if len(pose_window) < window_size:
119121
return False, None, None, None
120122

@@ -131,14 +133,14 @@ def fall_detection(pose_window, window_size, fps, v_thresh, aspect_ratio_thresh,
131133
f"ar={ar_delta:.2f}/{aspect_ratio_thresh:.2f}"
132134
)
133135
print(f"[TRACE] {debug_text}")
134-
136+
135137
cond_speed_drop = v > v_thresh and dy > dy_thresh
136138
cond_down_flat = dy > dy_thresh and ar_delta > aspect_ratio_thresh
137139

138140
if cond_speed_drop or cond_down_flat:
139141
tag = (
140-
("SpeedDrop " if cond_speed_drop else "") +
141-
("DownFlat " if cond_down_flat else "")
142+
("SpeedDrop " if cond_speed_drop else "")
143+
+ ("DownFlat " if cond_down_flat else "")
142144
).strip()
143145

144146
xmin = pose_end[2] - pose_end[4] / 2
@@ -171,8 +173,10 @@ def falling_alarm(image, bbox):
171173
lineType=cv2.LINE_AA,
172174
)
173175

176+
174177
def draw_fps(frame, prev_time):
175178
import time
179+
176180
curr_time = time.time()
177181
fps = 1 / (curr_time - prev_time)
178182
cv2.putText(
@@ -186,4 +190,3 @@ def draw_fps(frame, prev_time):
186190
cv2.LINE_AA,
187191
)
188192
return frame, curr_time
189-

fall_dataset/ci_videos/video_1.mp4

983 KB
Binary file not shown.

realtime.py

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
from fall_core import (
2-
get_pose_model, get_pose, prepare_image,
3-
fall_detection, falling_alarm, draw_fps,
2+
get_pose_model,
3+
get_pose,
4+
prepare_image,
5+
fall_detection,
6+
falling_alarm,
7+
draw_fps,
48
)
59
import cv2
610
import time
@@ -32,17 +36,22 @@ def process_realtime_camera():
3236
FPS,
3337
V_THRESH,
3438
ASPECT_RATIO_THRESH,
35-
DY_THRESH
39+
DY_THRESH,
3640
)
3741
if debug_text:
3842
_image = cv2.putText(
39-
_image, f"{tag}: {debug_text}", (10, 30), cv2.FONT_HERSHEY_SIMPLEX,
40-
0.7, (0, 255, 0), 2
43+
_image,
44+
f"{tag}: {debug_text}",
45+
(10, 30),
46+
cv2.FONT_HERSHEY_SIMPLEX,
47+
0.7,
48+
(0, 255, 0),
49+
2,
4150
)
4251
if is_fall:
4352
falling_alarm(_image, bbox)
4453

45-
cv2.imshow("Real-Time Fall Detection", _image[:,:,::-1])
54+
cv2.imshow("Real-Time Fall Detection", _image[:, :, ::-1])
4655
if cv2.waitKey(1) & 0xFF == ord("q"):
4756
break
4857

requirements.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,4 +41,6 @@ thop~=0.1.1.post2209072238
4141
# roboflow
4242

4343
telepot~=12.7
44-
torchmetrics~=0.10.2
44+
torchmetrics~=0.10.2
45+
46+
black==24.8.0

video.py

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
from fall_core import (
2-
get_pose_model, get_pose, prepare_image, fall_detection,
3-
falling_alarm, prepare_vid_out
2+
get_pose_model,
3+
get_pose,
4+
prepare_image,
5+
fall_detection,
6+
falling_alarm,
7+
prepare_vid_out,
48
)
59
import cv2
610
import os
@@ -31,29 +35,38 @@ def process_video_file(video_path, output_dir):
3135
if len(output) > 0:
3236
pose_window.append(output)
3337
if len(pose_window) == WINDOW_SIZE:
34-
is_fall, bbox, debug_text, tag= fall_detection(
38+
is_fall, bbox, debug_text, tag = fall_detection(
3539
pose_window,
3640
WINDOW_SIZE,
3741
FPS,
3842
V_THRESH,
3943
ASPECT_RATIO_THRESH,
40-
DY_THRESH
44+
DY_THRESH,
4145
)
4246
# debug
4347
_image = cv2.putText(
44-
_image, f"{tag}: {debug_text}", (10, 30), cv2.FONT_HERSHEY_SIMPLEX,
45-
0.7, (0, 255, 0), 2
48+
_image,
49+
f"{tag}: {debug_text}",
50+
(10, 30),
51+
cv2.FONT_HERSHEY_SIMPLEX,
52+
0.7,
53+
(0, 255, 0),
54+
2,
4655
)
4756
if is_fall:
4857
falling_alarm(_image, bbox)
49-
vid_out.write(_image[:,:,::-1])
58+
vid_out.write(_image[:, :, ::-1])
5059

5160
vid_out.release()
5261
vid_cap.release()
5362

5463

5564
if __name__ == "__main__":
56-
videos_path = "fall_dataset/videos"
65+
if os.environ.get("CI_MODE") == "1":
66+
videos_path = "fall_dataset/ci_videos"
67+
print("[CI MODE] Only running on CI test videos...")
68+
else:
69+
videos_path = "fall_dataset/videos"
5770
output_dir = "output_videos"
5871
os.makedirs(output_dir, exist_ok=True)
5972

0 commit comments

Comments
 (0)