ltlonggg commited on
Commit
794b89a
·
1 Parent(s): 76e0ae2

Add application file

Browse files
Files changed (5) hide show
  1. app.py +236 -0
  2. best_model_efficientnet_lstm_v2.pth +3 -0
  3. packages.txt +2 -0
  4. requirements.txt +7 -0
  5. yolov8n.pt +3 -0
app.py ADDED
@@ -0,0 +1,236 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import cv2
3
+ import numpy as np
4
+ import torch
5
+ import torch.nn as nn
6
+ from torchvision.models import efficientnet_v2_s, EfficientNet_V2_S_Weights
7
+ import albumentations as A
8
+ from ultralytics import YOLO
9
+ from collections import deque
10
+
11
+ # ============================================================
12
+ # 1. CẤU HÌNH MODEL (Giữ nguyên logic cũ)
13
+ # ============================================================
14
+ class EfficientNetLSTM(nn.Module):
15
+ def __init__(self, hidden_size=256, num_layers=2, dropout=0.5):
16
+ super(EfficientNetLSTM, self).__init__()
17
+ weights = EfficientNet_V2_S_Weights.IMAGENET1K_V1
18
+ self.efficientnet = efficientnet_v2_s(weights=weights)
19
+ num_features = self.efficientnet.classifier[1].in_features
20
+ self.efficientnet.classifier = nn.Identity()
21
+
22
+ self.lstm = nn.LSTM(
23
+ input_size=num_features,
24
+ hidden_size=hidden_size,
25
+ num_layers=num_layers,
26
+ batch_first=True,
27
+ dropout=dropout if num_layers > 1 else 0,
28
+ bidirectional=True
29
+ )
30
+
31
+ self.fc = nn.Sequential(
32
+ nn.Linear(256*2, 256),
33
+ nn.ReLU(inplace=True),
34
+ nn.Dropout(dropout),
35
+ nn.Linear(256, 128),
36
+ nn.ReLU(inplace=True),
37
+ nn.Dropout(dropout),
38
+ nn.Linear(128, 1)
39
+ )
40
+
41
+ def forward(self, x):
42
+ batch_size, num_frames, c, h, w = x.shape
43
+ x = x.view(batch_size * num_frames, c, h, w)
44
+ features = self.efficientnet(x)
45
+ features = features.view(batch_size, num_frames, -1)
46
+ lstm_out, _ = self.lstm(features)
47
+ final_features = lstm_out[:, -1, :]
48
+ output = self.fc(final_features)
49
+ return output.squeeze()
50
+
51
+ # Load Model (Global)
52
+ DEVICE = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
53
+ MODEL_PATH = "best_model_efficientnet_lstm_224_16.pth" # Đảm bảo file này nằm cùng thư mục
54
+
55
+ print("⏳ Đang tải models...")
56
+ try:
57
+ # Load Fall Detection Model
58
+ model = EfficientNetLSTM(hidden_size=256, num_layers=2, dropout=0.5).to(DEVICE)
59
+ model.load_state_dict(torch.load(MODEL_PATH, map_location=DEVICE))
60
+ model.eval()
61
+
62
+ # Load YOLO
63
+ yolo_model = YOLO("yolov8n.pt")
64
+ print("✅ Đã tải xong models!")
65
+ except Exception as e:
66
+ print(f"❌ Lỗi tải model: {e}")
67
+
68
+ # Transform
69
+ transform = A.Compose([
70
+ A.Resize(height=224, width=224),
71
+ A.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
72
+ A.ToTensorV2(),
73
+ ])
74
+
75
+ # ============================================================
76
+ # 2. HÀM XỬ LÝ LOGIC
77
+ # ============================================================
78
+
79
+ def preprocess_frame(frame_rgb):
80
+ """Chuyển đổi 1 frame ảnh thành tensor cho model"""
81
+ augmented = transform(image=frame_rgb)
82
+ return augmented['image']
83
+
84
+ def predict_webcam_stream(image, buffer_state, history_log):
85
+ """
86
+ Hàm này chạy liên tục cho mỗi frame từ webcam.
87
+ - image: Frame hiện tại từ webcam (numpy array)
88
+ - buffer_state: List chứa các frame trước đó (để đủ 16 frames)
89
+ - history_log: Text log lịch sử
90
+ """
91
+ if image is None:
92
+ return image, buffer_state, "Không có camera", history_log
93
+
94
+ # 1. Detect người bằng YOLO
95
+ results = yolo_model(image, verbose=False, conf=0.5)
96
+ boxes = results[0].boxes.data.cpu().numpy()
97
+
98
+ has_person = False
99
+ bbox = None
100
+
101
+ for x1, y1, x2, y2, conf, cls in boxes:
102
+ if int(cls) == 0: # Person class
103
+ has_person = True
104
+ bbox = (int(x1), int(y1), int(x2), int(y2))
105
+ break # Chỉ lấy người đầu tiên
106
+
107
+ status_text = "Đang chờ tín hiệu..."
108
+ color = (0, 255, 0) # Green
109
+
110
+ # 2. Xử lý Buffer cho LSTM
111
+ # Chuyển đổi ảnh sang Tensor và thêm vào buffer
112
+ frame_tensor = preprocess_frame(image)
113
+
114
+ # buffer_state là một list, ta quản lý nó như deque
115
+ current_buffer = buffer_state if buffer_state is not None else []
116
+ current_buffer.append(frame_tensor)
117
+
118
+ if len(current_buffer) > 16:
119
+ current_buffer.pop(0) # Xóa frame cũ nhất
120
+
121
+ # 3. Dự đoán Fall (Chỉ khi đủ 16 frames và có người)
122
+ if len(current_buffer) == 16:
123
+ # Gom buffer thành batch
124
+ video_tensor = torch.stack(current_buffer).unsqueeze(0).to(DEVICE)
125
+
126
+ with torch.no_grad():
127
+ output = model(video_tensor)
128
+ prob = torch.sigmoid(output).item()
129
+
130
+ is_fall = prob > 0.5
131
+
132
+ if is_fall:
133
+ status_text = f"⚠️ PHÁT HIỆN NGÃ! ({prob*100:.1f}%)"
134
+ color = (0, 0, 255) # Red
135
+ history_log = f"🔴 Ngã - {prob*100:.0f}%\n" + history_log
136
+ else:
137
+ status_text = f"✅ An toàn ({prob*100:.1f}%)"
138
+ color = (0, 255, 0) # Green
139
+ # history_log = f"🟢 An toàn - {prob*100:.0f}%\n" + history_log # (Tắt dòng này cho đỡ spam log)
140
+
141
+ # 4. Vẽ Bounding Box
142
+ if bbox:
143
+ x1, y1, x2, y2 = bbox
144
+ cv2.rectangle(image, (x1, y1), (x2, y2), color, 3)
145
+ cv2.putText(image, status_text, (x1, y1 - 10),
146
+ cv2.FONT_HERSHEY_SIMPLEX, 0.8, color, 2)
147
+ else:
148
+ cv2.putText(image, "Khong thay nguoi", (20, 50),
149
+ cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 0), 2)
150
+
151
+ # Giới hạn log
152
+ if len(history_log) > 1000: history_log = history_log[:1000]
153
+
154
+ return image, current_buffer, status_text, history_log
155
+
156
+ def analyze_uploaded_video(video_path):
157
+ """Phân tích video upload lên"""
158
+ cap = cv2.VideoCapture(video_path)
159
+ frames = []
160
+ fps = cap.get(cv2.CAP_PROP_FPS)
161
+ total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
162
+
163
+ # Lấy 16 frames rải đều
164
+ if total_frames >= 16:
165
+ indices = np.linspace(0, total_frames - 1, 16, dtype=int)
166
+ else:
167
+ indices = np.arange(total_frames)
168
+ # Pad nếu thiếu (đơn giản hóa)
169
+
170
+ for i in range(total_frames):
171
+ ret, frame = cap.read()
172
+ if not ret: break
173
+ if i in indices:
174
+ frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
175
+ frames.append(preprocess_frame(frame))
176
+
177
+ cap.release()
178
+
179
+ if len(frames) < 16:
180
+ # Pad frame cuối nếu thiếu
181
+ while len(frames) < 16:
182
+ frames.append(frames[-1])
183
+
184
+ video_tensor = torch.stack(frames).unsqueeze(0).to(DEVICE)
185
+
186
+ with torch.no_grad():
187
+ output = model(video_tensor)
188
+ prob = torch.sigmoid(output).item()
189
+
190
+ if prob > 0.5:
191
+ return f"⚠️ CẢNH BÁO: Video có hành động NGÃ.\nTỷ lệ: {prob*100:.2f}%"
192
+ else:
193
+ return f"✅ Video AN TOÀN.\nTỷ lệ ngã: {prob*100:.2f}%"
194
+
195
+ # ============================================================
196
+ # 3. GIAO DIỆN GRADIO
197
+ # ============================================================
198
+
199
+ with gr.Blocks(title="Hệ thống Fall Detection", theme=gr.themes.Soft()) as demo:
200
+ gr.Markdown("# 🎥 Hệ thống Phát hiện Té ngã (EfficientNet + LSTM)")
201
+
202
+ with gr.Tab("📹 Camera Realtime"):
203
+ with gr.Row():
204
+ with gr.Column(scale=7):
205
+ # Input Webcam: streaming=True để gửi liên tục
206
+ input_cam = gr.Image(source="webcam", streaming=True, label="Camera")
207
+ output_cam = gr.Image(label="Kết quả xử lý")
208
+
209
+ with gr.Column(scale=3):
210
+ status_label = gr.Label(label="Trạng thái hiện tại")
211
+ log_box = gr.Textbox(label="Nhật ký phát hiện", lines=10)
212
+
213
+ # Biến State để lưu buffer frames giữa các lần gọi hàm
214
+ buffer_state = gr.State([])
215
+
216
+ # Sự kiện: Khi input cam thay đổi -> gọi hàm xử lý -> cập nhật output
217
+ input_cam.change(
218
+ fn=predict_webcam_stream,
219
+ inputs=[input_cam, buffer_state, log_box],
220
+ outputs=[output_cam, buffer_state, status_label, log_box],
221
+ show_progress=False
222
+ )
223
+
224
+ with gr.Tab("📂 Phân tích Video File"):
225
+ video_input = gr.Video(label="Tải video lên")
226
+ analyze_btn = gr.Button("Phân tích Video", variant="primary")
227
+ result_text = gr.Textbox(label="Kết quả")
228
+
229
+ analyze_btn.click(
230
+ fn=analyze_uploaded_video,
231
+ inputs=video_input,
232
+ outputs=result_text
233
+ )
234
+
235
+ if __name__ == "__main__":
236
+ demo.launch()
best_model_efficientnet_lstm_v2.pth ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:c80bc58183c7c98639b2fee7fa441496f3d3fec20eaf03939402546e215fe4c3
3
+ size 101199140
packages.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ libgl1
2
+ libglib2.0-0
requirements.txt ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ gradio
2
+ numpy
3
+ opencv-python-headless
4
+ torch
5
+ torchvision
6
+ albumentations
7
+ ultralytics
yolov8n.pt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:f59b3d833e2ff32e194b5bb8e08d211dc7c5bdf144b90d2c8412c47ccfc83b36
3
+ size 6549796