Saiky2k commited on
Commit
b92ea3d
·
verified ·
1 Parent(s): 606a3bb

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +221 -375
app.py CHANGED
@@ -11,459 +11,305 @@ import os
11
  import requests
12
  from io import BytesIO
13
 
14
- # Cấu hình trang
15
- st.set_page_config(page_title="Phát hiện người và độ sâu", layout="wide")
16
-
17
- # Giả lập module depth_pro
18
- class Depth_pro:
19
  @staticmethod
20
  def create_model_and_transforms():
21
- # Import thư viện cần thiết
22
  import torch
23
  from transformers import AutoImageProcessor, AutoModelForDepthEstimation
24
-
25
- # Tải processor model
26
  processor = AutoImageProcessor.from_pretrained("vinvino02/glpn-nyu")
27
  model = AutoModelForDepthEstimation.from_pretrained("vinvino02/glpn-nyu")
28
 
29
- # Thiết lập model đưa về chế độ đánh giá
30
- model.eval()
31
-
32
- # Hàm transform cho ảnh đầu vào
33
  def transform(image):
34
  return processor(images=image, return_tensors="pt").pixel_values
35
 
36
- # Mở rộng model với phương thức infer (tương thích với mã ban đầu)
37
  def infer_method(self, image, f_px=None):
38
  with torch.no_grad():
39
  outputs = self(image)
40
  predicted_depth = outputs.predicted_depth
41
-
42
  # Chuẩn hóa độ sâu
43
  depth_min = torch.min(predicted_depth)
44
- depth_max = torch.max(predicted_depth)
45
  predicted_depth = (predicted_depth - depth_min) / (depth_max - depth_min)
46
- predicted_depth = predicted_depth * 10 # Nhân với 10 để có giá trị mét hợp lý
47
 
48
  return {"depth": predicted_depth}
49
 
50
- # Gắn phương thức infer vào model
51
  model.infer = infer_method.__get__(model)
52
 
53
  return model, transform
54
 
55
- # Tải model YOLO cache
56
  @st.cache_resource
57
  def load_yolo_model():
 
58
  model = YOLO("yolov8n.pt")
59
  return model
60
 
61
- # Tạo bản đồ màu từ ảnh độ sâu
62
- def create_depth_colormap(depth_map):
63
- # Chuẩn hóa độ sâu
64
- depth_np_normalized = (depth_map - np.min(depth_map)) / (np.max(depth_map) - np.min(depth_map))
65
- inv_depth_np_normalized = 1 - depth_np_normalized # Đảo ngược (gần = sáng, xa = tối)
 
 
 
 
 
 
 
66
 
67
- # Chuyển đổi sang bản đồ màu
68
- depth_colormap = cv2.applyColorMap((inv_depth_np_normalized * 255).astype(np.uint8), cv2.COLORMAP_TURBO)
 
 
 
69
 
70
- # Chuyển đổi từ BGR sang RGB
71
- depth_colormap_rgb = cv2.cvtColor(depth_colormap, cv2.COLOR_BGR2RGB)
 
 
 
 
72
 
73
- return depth_colormap_rgb
74
-
75
- # Vẽ nhãn trên ảnh
76
- def draw_depth_label(image, text, position):
77
- x1, y1 = position
78
- font = cv2.FONT_HERSHEY_SIMPLEX
79
- font_scale = 0.7
80
- font_thickness = 2
81
- text_size = cv2.getTextSize(text, font, font_scale, font_thickness)[0]
82
 
83
- # Vẽ hình chữ nhật nền
84
- text_x = x1
85
- text_y = y1 - 10
86
- rect_x1 = text_x - 5
87
- rect_y1 = text_y - text_size[1] - 5
88
- rect_x2 = text_x + text_size[0] + 5
89
- rect_y2 = text_y + 5
90
- cv2.rectangle(image, (rect_x1, rect_y1), (rect_x2, rect_y2), (0, 255, 0), -1)
91
 
92
- # Vẽ văn bản
93
- cv2.putText(image, text, (text_x, text_y), font, font_scale, (0, 0, 0), font_thickness)
94
-
95
- # Chức năng xử lý ảnh
96
- def process_image():
97
- st.header("Phát hiện người và Ước tính độ sâu trong Ảnh")
98
 
99
- # Tùy chọn cho ảnh
100
- upload_option = st.radio("Chọn nguồn ảnh:", ["Tải lên ảnh", "Sử dụng ảnh mẫu"])
 
 
101
 
102
- image = None
 
103
 
104
- if upload_option == "Tải lên ảnh":
105
- uploaded_image = st.file_uploader("Tải lên ảnh", type=["jpg", "jpeg", "png"])
106
- if uploaded_image is not None:
107
- image = Image.open(uploaded_image)
108
- image_np = np.array(image)
109
-
110
- # Hiển thị ảnh gốc
111
- st.image(image_np, caption="Ảnh đã tải lên", use_container_width=True)
112
- else:
113
- # Sử dụng ảnh mẫu
114
- sample_img_url = "https://storage.googleapis.com/sfr-vision-language-research/DINO/ground_truth_images/000000014439.jpg"
115
- try:
116
- response = requests.get(sample_img_url)
117
- image = Image.open(BytesIO(response.content))
118
- image_np = np.array(image)
119
-
120
- # Hiển thị ảnh mẫu
121
- st.image(image_np, caption="Ảnh mẫu", use_container_width=True)
122
- except Exception as e:
123
- st.error(f"Không thể tải ảnh mẫu: {e}")
124
 
125
- # Ngưỡng tin cậy cho phát hiện
126
- confidence = st.slider("Ngưỡng tin cậy:", 0.0, 1.0, 0.5, 0.05)
127
 
128
- # Chỉ tiếp tục nếu ảnh
129
- if image is not None and st.button("Xử lý Ảnh"):
130
- with st.spinner("Đang xử lý ảnh..."):
131
- device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
132
- st.info(f"Đang sử dụng thiết bị: {device}")
133
-
134
- # Tải models
135
- yolo_model = load_yolo_model()
136
- depth_pro = Depth_pro()
137
- depth_model, transform = depth_pro.create_model_and_transforms()
138
- if device.type == 'cuda':
139
- depth_model.to(device)
 
 
 
 
 
140
 
141
- # Phát hiện người
142
- results = yolo_model(image_np, conf=confidence)
 
143
 
144
- # Chuẩn bị ảnh để vẽ kết quả
145
- output_image = image_np.copy()
 
 
 
146
 
147
- # Chuẩn bị đầu vào cho model độ sâu
148
- if len(image_np.shape) == 3 and image_np.shape[2] == 3:
149
- if image_np.dtype == np.uint8:
150
- rgb_image = cv2.cvtColor(image_np, cv2.COLOR_BGR2RGB) if image_np.shape[2] == 3 else image_np
151
- pil_image = Image.fromarray(rgb_image)
152
- else:
153
- pil_image = image
154
  else:
155
- pil_image = image
156
 
157
- # Chuyển đổi ảnh cho model độ sâu
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
158
  depth_input = transform(pil_image)
 
159
  if device.type == 'cuda':
160
  depth_input = depth_input.to(device)
161
 
162
  # Ước tính độ sâu
163
- focal_length_px = torch.tensor([max(image_np.shape[1], image_np.shape[0])], device=device)
164
  with torch.no_grad():
165
  predictions = depth_model.infer(depth_input, f_px=focal_length_px)
166
- depth = predictions["depth"]
167
 
168
- # Chuyển tensor sang numpy
169
  depth_np = depth.squeeze().cpu().numpy()
170
 
171
- # Điều chỉnh kích thước bản đồ độ sâu nếu cần
172
- if depth_np.shape[:2] != image_np.shape[:2]:
173
- depth_np = cv2.resize(depth_np, (image_np.shape[1], image_np.shape[0]), interpolation=cv2.INTER_LINEAR)
174
 
175
  # Tạo bản đồ màu độ sâu
176
- depth_colormap = create_depth_colormap(depth_np)
177
-
178
- # Đếm số người phát hiện được
179
- person_count = 0
180
 
181
- # Xử kết quả YOLO
182
- for result in results:
183
- boxes = result.boxes.xyxy.cpu().numpy()
184
- classes = result.boxes.cls.cpu().numpy()
185
- confs = result.boxes.conf.cpu().numpy()
186
 
187
- for box, cls, conf in zip(boxes, classes, confs):
188
- if result.names[int(cls)] == "person" and conf > confidence:
189
- person_count += 1
190
- x1, y1, x2, y2 = map(int, box[:4])
191
-
192
- # Vẽ khung giới hạn
193
- cv2.rectangle(output_image, (x1, y1), (x2, y2), (0, 255, 0), 2)
194
-
195
- # Tính độ sâu vị trí trung tâm
196
- center_x = (x1 + x2) // 2
197
- center_y = (y1 + y2) // 2
198
-
199
- # Đảm bảo tọa độ trong giới hạn
200
- center_x = min(center_x, depth_np.shape[1] - 1)
201
- center_y = min(center_y, depth_np.shape[0] - 1)
202
-
203
- depth_value = depth_np[center_y, center_x]
204
-
205
- # Vẽ thông tin độ sâu
206
- text = f"Độ sâu: {depth_value:.2f}m"
207
- draw_depth_label(output_image, text, (x1, y1))
208
 
209
- # Hiển thị kết quả
210
- st.success(f"Đã phát hiện {person_count} người trong ảnh")
 
 
211
 
212
- col1, col2 = st.columns(2)
213
- col1.image(output_image, caption="Phát hiện người với độ sâu", use_container_width=True)
214
- col2.image(depth_colormap, caption="Bản đồ độ sâu", use_container_width=True)
 
 
 
 
 
 
 
 
 
 
 
 
215
 
216
- # Chức năng xử lý video
217
- def process_video():
218
- st.header("Phát hiện người và Ước tính độ sâu trong Video")
 
219
 
220
- # Tùy chọn cho video
221
- upload_option = st.radio("Chọn nguồn video:", ["Tải lên video", "Sử dụng video mẫu"])
 
222
 
223
  video_path = None
224
-
225
- if upload_option == "Tải lên video":
226
- uploaded_video = st.file_uploader("Tải lên video", type=["mp4", "avi", "mov"])
227
- if uploaded_video is not None:
228
- # Lưu video tải lên vào tệp tạm thời
229
- temp_file = tempfile.NamedTemporaryFile(delete=False, suffix='.mp4')
230
- temp_file.write(uploaded_video.read())
231
- video_path = temp_file.name
232
- temp_file.close()
233
-
234
- # Hiển thị video gốc
235
- st.video(video_path)
236
- else:
237
- # Sử dụng video mẫu
238
- sample_video_url = "https://huggingface.co/spaces/Nupoor/SampleVideoDataset/resolve/main/pexels-richard-de-souza-1635985.mp4"
239
- try:
240
- # Tải video mẫu
241
- response = requests.get(sample_video_url)
242
  temp_file = tempfile.NamedTemporaryFile(delete=False, suffix='.mp4')
243
- temp_file.write(response.content)
244
  video_path = temp_file.name
245
  temp_file.close()
246
 
247
- # Hiển thị video mẫu
248
  st.video(video_path)
249
- except Exception as e:
250
- st.error(f"Không thể tải video mẫu: {e}")
251
 
252
- # Ngưỡng tin cậy cho phát hiện
253
- confidence = st.slider("Ngưỡng tin cậy:", 0.0, 1.0, 0.5, 0.05)
 
 
 
 
254
 
255
- # Mỗi bao nhiêu khung hình thì cập nhật độ sâu
256
- depth_update_interval = st.slider("Cập nhật độ sâu mỗi (số khung hình):", 1, 10, 5)
257
 
258
- # Chỉ tiếp tục nếu video
259
- if video_path is not None and st.button("Xử lý Video"):
260
- # Hiển thị thanh tiến trình
261
- progress_bar = st.progress(0)
262
- status_text = st.empty()
263
 
264
- device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
265
- st.info(f"Đang sử dụng thiết bị: {device}")
266
 
267
- # Tải models
268
- with st.spinner('Đang tải mô hình YOLO...'):
269
- yolo_model = load_yolo_model()
270
- if device.type == 'cuda':
271
- yolo_model.to(device)
272
-
273
- with st.spinner('Đang tải mô hình độ sâu...'):
274
- depth_pro = Depth_pro()
275
- depth_model, transform = depth_pro.create_model_and_transforms()
276
- if device.type == 'cuda':
277
- depth_model.to(device)
278
 
279
- # Mở video
280
- cap = cv2.VideoCapture(video_path)
 
 
 
 
 
 
281
 
282
- # Lấy thông tin video
283
- width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
284
- height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
285
- fps = cap.get(cv2.CAP_PROP_FPS)
286
- total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
287
-
288
- # Tạo tệp đầu ra
289
- temp_output_dir = tempfile.mkdtemp()
290
- output_video_path = os.path.join(temp_output_dir, "person_detection_with_depth.mp4")
291
- output_depth_path = os.path.join(temp_output_dir, "depth_colormap.mp4")
292
-
293
- # Thiết lập writers
294
- fourcc = cv2.VideoWriter_fourcc(*'XVID')
295
- out_detection = cv2.VideoWriter(output_video_path, fourcc, fps, (width, height))
296
- out_depth = cv2.VideoWriter(output_depth_path, fourcc, fps, (width, height))
297
-
298
- # Ước tính chiều dài tiêu cự
299
- focal_length_px = torch.tensor([max(width, height)], device=device)
300
-
301
- # Cột hiển thị khung hình đang xử lý
302
- preview_col1, preview_col2 = st.columns(2)
303
- detection_placeholder = preview_col1.empty()
304
- depth_placeholder = preview_col2.empty()
305
-
306
- frame_counter = 0
307
- start_time = time.time()
308
- depth_np = None
309
-
310
- try:
311
- while cap.isOpened():
312
- ret, frame = cap.read()
313
- if not ret:
314
- break
315
-
316
- frame_counter += 1
317
-
318
- # Cập nhật tiến trình
319
- progress = int(frame_counter / total_frames * 100)
320
- progress_bar.progress(progress)
321
-
322
- if frame_counter % 10 == 0:
323
- elapsed_time = time.time() - start_time
324
- frames_left = total_frames - frame_counter
325
- est_time_left = (elapsed_time / frame_counter) * frames_left if frame_counter > 0 else 0
326
- status_text.text(f"Đang xử lý khung hình {frame_counter}/{total_frames} - Thời gian còn lại: {est_time_left:.2f}s")
327
-
328
- # Phát hiện người với YOLO
329
- results = yolo_model(frame, conf=confidence)
330
-
331
- person_boxes = []
332
- for result in results:
333
- boxes = result.boxes.xyxy.cpu().numpy()
334
- classes = result.boxes.cls.cpu().numpy()
335
- confs = result.boxes.conf.cpu().numpy()
336
-
337
- for box, cls, conf in zip(boxes, classes, confs):
338
- if result.names[int(cls)] == "person" and conf > confidence:
339
- x1, y1, x2, y2 = map(int, box[:4])
340
- person_boxes.append((x1, y1, x2, y2))
341
- cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 255, 0), 2)
342
-
343
- # Cập nhật độ sâu theo khoảng đã thiết lập
344
- if frame_counter % depth_update_interval == 0 or frame_counter == 1 or depth_np is None:
345
- # Chuyển đổi khung hình cho model độ sâu
346
- rgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
347
- pil_image = Image.fromarray(rgb_frame)
348
- depth_input = transform(pil_image)
349
-
350
- if device.type == 'cuda':
351
- depth_input = depth_input.to(device)
352
-
353
- # Ước tính độ sâu
354
- with torch.no_grad():
355
- predictions = depth_model.infer(depth_input, f_px=focal_length_px)
356
- depth = predictions["depth"]
357
-
358
- depth_np = depth.squeeze().cpu().numpy()
359
-
360
- # Điều chỉnh kích thước nếu cần
361
- if depth_np.shape[:2] != (height, width):
362
- depth_np = cv2.resize(depth_np, (width, height), interpolation=cv2.INTER_LINEAR)
363
-
364
- # Tạo bản đồ màu độ sâu
365
- depth_colormap = create_depth_colormap(depth_np)
366
-
367
- # Thêm thông tin độ sâu cho người đã phát hiện
368
- for x1, y1, x2, y2 in person_boxes:
369
- center_x = (x1 + x2) // 2
370
- center_y = (y1 + y2) // 2
371
-
372
- # Đảm bảo tọa độ trong giới hạn
373
- center_x = min(center_x, depth_np.shape[1] - 1)
374
- center_y = min(center_y, depth_np.shape[0] - 1)
375
-
376
- depth_value = depth_np[center_y, center_x]
377
-
378
- # Vẽ thông tin độ sâu
379
- text = f"Độ sâu: {depth_value:.2f}m"
380
- draw_depth_label(frame, text, (x1, y1))
381
-
382
- # Hiển thị khung hình trong Streamlit (cập nhật theo khoảng)
383
- if frame_counter % 5 == 0 or frame_counter == 1:
384
- detection_placeholder.image(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB), caption="Phát hiện người", use_container_width=True)
385
- depth_placeholder.image(depth_colormap, caption="Bản đồ độ sâu", use_container_width=True)
386
-
387
- # Ghi khung hình vào videos
388
- out_detection.write(frame)
389
- out_depth.write(cv2.cvtColor(depth_colormap, cv2.COLOR_RGB2BGR))
390
-
391
- finally:
392
- # Giải phóng tài nguyên
393
- cap.release()
394
- out_detection.release()
395
- out_depth.release()
396
-
397
- total_time = time.time() - start_time
398
- st.success(f"Xử lý hoàn tất! Tổng thời gian: {total_time:.2f}s")
399
- st.success(f"FPS trung bình: {frame_counter / total_time:.2f}")
400
-
401
- # Hiển thị videos đã xử lý
402
- st.subheader("Videos kết quả")
403
-
404
- col1, col2 = st.columns(2)
405
- with col1:
406
- st.video(output_video_path)
407
- st.download_button(
408
- label="Tải xuống video phát hiện",
409
- data=open(output_video_path, 'rb').read(),
410
- file_name="person_detection_with_depth.mp4",
411
- mime="video/mp4"
412
- )
413
-
414
- with col2:
415
- st.video(output_depth_path)
416
- st.download_button(
417
- label="Tải xuống bản đồ độ sâu",
418
- data=open(output_depth_path, 'rb').read(),
419
- file_name="depth_colormap.mp4",
420
- mime="video/mp4"
421
- )
422
-
423
- # Xóa tệp tạm thời
424
- try:
425
- os.unlink(video_path)
426
- except:
427
- pass
428
-
429
- # Giao diện chính
430
- def main():
431
- st.title("Ứng dụng Phát hiện Người và Ước tính Độ sâu")
432
-
433
- # Chọn chế độ xử lý
434
- app_mode = st.sidebar.selectbox("Chọn chế độ:", ["Xử lý Ảnh", "Xử lý Video"])
435
-
436
- # Hiển thị thông tin
437
- st.sidebar.header("Thông tin")
438
- st.sidebar.info("""
439
- **Mô hình sử dụng:**
440
- - Phát hiện người: YOLOv8n
441
- - Độ sâu: depth_pro (GLPN-NYU)
442
-
443
- **Màu sắc trong bản đồ độ sâu:**
444
- - Màu đỏ/vàng: Gần
445
- - Màu xanh: Xa
446
- """)
447
-
448
- # Chạy chức năng tương ứng
449
- if app_mode == "Xử lý Ảnh":
450
- process_image()
451
- else:
452
- process_video()
453
 
454
- # Tạo tệp requirements.txt cho Hugging Face Space
455
  def create_requirements():
456
- return """
457
- streamlit
458
- numpy
459
- Pillow
460
- opencv-python-headless
461
- torch
462
- torchvision
463
- transformers
464
- ultralytics
465
- requests
466
- """
 
 
467
 
468
  if __name__ == "__main__":
469
  main()
 
11
  import requests
12
  from io import BytesIO
13
 
14
+ # Tạo module depth_pro đơn giản (để thay thế module gốc)
15
+ class DepthPro:
 
 
 
16
  @staticmethod
17
  def create_model_and_transforms():
18
+ # Nhập các thư viện cần thiết ở đây để tránh lỗi khi khởi tạo
19
  import torch
20
  from transformers import AutoImageProcessor, AutoModelForDepthEstimation
21
+
22
+ # Tải hình depth estimation từ Hugging Face
23
  processor = AutoImageProcessor.from_pretrained("vinvino02/glpn-nyu")
24
  model = AutoModelForDepthEstimation.from_pretrained("vinvino02/glpn-nyu")
25
 
26
+ # Tạo hàm transform đơn giản
 
 
 
27
  def transform(image):
28
  return processor(images=image, return_tensors="pt").pixel_values
29
 
30
+ # Mở rộng model với phương thức infer
31
  def infer_method(self, image, f_px=None):
32
  with torch.no_grad():
33
  outputs = self(image)
34
  predicted_depth = outputs.predicted_depth
35
+
36
  # Chuẩn hóa độ sâu
37
  depth_min = torch.min(predicted_depth)
38
+ depth_max = torch.max(predicted_depth)
39
  predicted_depth = (predicted_depth - depth_min) / (depth_max - depth_min)
40
+ predicted_depth = predicted_depth * 10 # Nhân với 10 để có giá trị mét hợp lý hơn
41
 
42
  return {"depth": predicted_depth}
43
 
44
+ # Thêm phương thức infer vào model
45
  model.infer = infer_method.__get__(model)
46
 
47
  return model, transform
48
 
49
+ # Hàm tải mô hình YOLO từ Hugging Face
50
  @st.cache_resource
51
  def load_yolo_model():
52
+ # Sử dụng mô hình YOLOv8n từ Hugging Face
53
  model = YOLO("yolov8n.pt")
54
  return model
55
 
56
+ # Hàm tải chuẩn bị hình độ sâu
57
+ @st.cache_resource
58
+ def load_depth_model():
59
+ depth_pro = DepthPro()
60
+ model, transform = depth_pro.create_model_and_transforms()
61
+ return model, transform
62
+
63
+ # Hàm xử lý video
64
+ def process_video(video_path):
65
+ # Kiểm tra CUDA
66
+ device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
67
+ st.info(f"Đang sử dụng thiết bị: {device}")
68
 
69
+ # Tải hình YOLO
70
+ with st.spinner('Đang tải hình YOLO...'):
71
+ yolo_model = load_yolo_model()
72
+ if device.type == 'cuda':
73
+ yolo_model.to(device)
74
 
75
+ # Tải hình độ sâu
76
+ with st.spinner('Đang tải mô hình độ sâu...'):
77
+ depth_model, transform = load_depth_model()
78
+ depth_model.eval()
79
+ if device.type == 'cuda':
80
+ depth_model.to(device)
81
 
82
+ # Mở video để xử lý
83
+ cap = cv2.VideoCapture(video_path)
 
 
 
 
 
 
 
84
 
85
+ # Lấy thuộc tính video cho đầu ra
86
+ width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
87
+ height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
88
+ fps = cap.get(cv2.CAP_PROP_FPS)
89
+ total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
 
 
 
90
 
91
+ # Tạo tệp tạm thời cho video đầu ra
92
+ temp_output_dir = tempfile.mkdtemp()
93
+ output_video_path = os.path.join(temp_output_dir, "person_detection_with_depth.mp4")
94
+ output_depth_path = os.path.join(temp_output_dir, "depth_colormap.mp4")
 
 
95
 
96
+ # Sử dụng codec phù hợp với môi trường Hugging Face
97
+ fourcc = cv2.VideoWriter_fourcc(*'XVID') # Thay đổi từ mp4v sang XVID cho tương thích tốt hơn
98
+ out_detection = cv2.VideoWriter(output_video_path, fourcc, fps, (width, height))
99
+ out_depth = cv2.VideoWriter(output_depth_path, fourcc, fps, (width, height))
100
 
101
+ # Ước tính chiều dài tiêu cự và chuyển đổi sang tensor
102
+ focal_length_px = torch.tensor([max(width, height)], device=device)
103
 
104
+ # Hiển thị thanh tiến trình
105
+ progress_bar = st.progress(0)
106
+ progress_text = st.empty()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
107
 
108
+ frame_counter = 0
109
+ start_time = time.time()
110
 
111
+ # Tạo cột để hiển thị khung video
112
+ col1, col2 = st.columns(2)
113
+ detection_placeholder = col1.empty()
114
+ depth_placeholder = col2.empty()
115
+
116
+ # Giảm kích thước frame để tăng tốc độ xử lý
117
+ target_width = 640 # Kích thước đích
118
+ scale_factor = target_width / width
119
+ target_height = int(height * scale_factor)
120
+
121
+ try:
122
+ while cap.isOpened():
123
+ ret, frame = cap.read()
124
+ if not ret:
125
+ break
126
+
127
+ frame_counter += 1
128
 
129
+ # Cập nhật tiến trình
130
+ progress = int(frame_counter / total_frames * 100)
131
+ progress_bar.progress(progress)
132
 
133
+ if frame_counter % 10 == 0: # Hiển thị tiến trình mỗi 10 khung hình
134
+ elapsed_time = time.time() - start_time
135
+ frames_left = total_frames - frame_counter
136
+ est_time_left = (elapsed_time / frame_counter) * frames_left if frame_counter > 0 else 0
137
+ progress_text.text(f"Đang xử lý khung hình {frame_counter}/{total_frames} - Thời gian còn lại: {est_time_left:.2f}s")
138
 
139
+ # Giảm kích thước khung hình để tăng tốc xử lý
140
+ if scale_factor < 1:
141
+ frame_resized = cv2.resize(frame, (target_width, target_height))
 
 
 
 
142
  else:
143
+ frame_resized = frame
144
 
145
+ # Phát hiện YOLO
146
+ results = yolo_model(frame_resized)
147
+
148
+ person_boxes = []
149
+ for result in results:
150
+ boxes = result.boxes.xyxy.cpu().numpy()
151
+ classes = result.boxes.cls.cpu().numpy()
152
+ confs = result.boxes.conf.cpu().numpy()
153
+
154
+ for box, cls, conf in zip(boxes, classes, confs):
155
+ if result.names[int(cls)] == "person" and conf > 0.5: # Thêm ngưỡng tin cậy
156
+ if scale_factor < 1: # Điều chỉnh lại khung giới hạn nếu đã thay đổi kích thước
157
+ x1, y1, x2, y2 = map(int, [box[0]/scale_factor, box[1]/scale_factor,
158
+ box[2]/scale_factor, box[3]/scale_factor])
159
+ else:
160
+ x1, y1, x2, y2 = map(int, box[:4])
161
+ person_boxes.append((x1, y1, x2, y2))
162
+ cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 255, 0), 2)
163
+
164
+ # Chuyển đổi khung hình cho đầu vào mô hình độ sâu
165
+ rgb_frame = cv2.cvtColor(frame_resized, cv2.COLOR_BGR2RGB)
166
+ pil_image = Image.fromarray(rgb_frame)
167
  depth_input = transform(pil_image)
168
+
169
  if device.type == 'cuda':
170
  depth_input = depth_input.to(device)
171
 
172
  # Ước tính độ sâu
 
173
  with torch.no_grad():
174
  predictions = depth_model.infer(depth_input, f_px=focal_length_px)
175
+ depth = predictions["depth"] # Độ sâu theo [m]
176
 
 
177
  depth_np = depth.squeeze().cpu().numpy()
178
 
179
+ # Điều chỉnh lại kích thước bản đồ độ sâu
180
+ if scale_factor < 1:
181
+ depth_np = cv2.resize(depth_np, (width, height), interpolation=cv2.INTER_LINEAR)
182
 
183
  # Tạo bản đồ màu độ sâu
184
+ depth_np_normalized = (depth_np - depth_np.min()) / (depth_np.max() - depth_np.min())
185
+ inv_depth_np_normalized = 1 - depth_np_normalized
186
+ depth_colormap = cv2.applyColorMap((inv_depth_np_normalized * 255).astype(np.uint8), cv2.COLORMAP_TURBO)
 
187
 
188
+ # Thêm giá trị độ sâu cho người được phát hiện
189
+ for x1, y1, x2, y2 in person_boxes:
190
+ center_x = (x1 + x2) // 2
191
+ center_y = (y1 + y2) // 2
 
192
 
193
+ # Đảm bảo tọa độ nằm trong giới hạn
194
+ center_x = min(center_x, depth_np.shape[1] - 1)
195
+ center_y = min(center_y, depth_np.shape[0] - 1)
196
+
197
+ depth_value = depth_np[center_y, center_x]
198
+
199
+ text = f"Độ sâu: {depth_value:.2f} m"
200
+ font = cv2.FONT_HERSHEY_SIMPLEX
201
+ font_scale = 0.8 # Giảm kích thước font để phù hợp
202
+ font_thickness = 2
203
+ text_size = cv2.getTextSize(text, font, font_scale, font_thickness)[0]
204
+
205
+ text_x = x1
206
+ text_y = y1 - 10
207
+ rect_x1 = text_x - 5
208
+ rect_y1 = text_y - text_size[1] - 10
209
+ rect_x2 = text_x + text_size[0] + 5
210
+ rect_y2 = text_y + 5
211
+
212
+ cv2.rectangle(frame, (rect_x1, rect_y1), (rect_x2, rect_y2), (0, 255, 0), -1)
213
+ cv2.putText(frame, text, (text_x, text_y), font, font_scale, (0, 0, 0), font_thickness)
214
 
215
+ # Hiển thị khung hình trong Streamlit (cập nhật mỗi 5 khung hình để tránh làm chậm)
216
+ if frame_counter % 5 == 0:
217
+ detection_placeholder.image(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB), caption="Phát hiện người", use_column_width=True)
218
+ depth_placeholder.image(depth_colormap, caption="Bản đồ độ sâu", use_column_width=True)
219
 
220
+ # Ghi khung hình vào video đầu ra
221
+ out_detection.write(frame)
222
+ out_depth.write(depth_colormap)
223
+
224
+ finally:
225
+ # Giải phóng tài nguyên
226
+ cap.release()
227
+ out_detection.release()
228
+ out_depth.release()
229
+
230
+ total_time = time.time() - start_time
231
+ st.success(f"Xử lý hoàn tất! Tổng thời gian: {total_time:.2f}s")
232
+ st.success(f"FPS trung bình: {frame_counter / total_time:.2f}")
233
+
234
+ return output_video_path, output_depth_path
235
 
236
+ # Giao diện Streamlit chính
237
+ def main():
238
+ st.title("Ứng dụng Phát hiện Người và Ước tính Độ sâu")
239
+ st.write("Tải lên video để phát hiện người và hiển thị thông tin độ sâu")
240
 
241
+ # Tùy chọn video mẫu
242
+ st.sidebar.header("Tùy chọn")
243
+ use_sample = st.sidebar.checkbox("Sử dụng video mẫu")
244
 
245
  video_path = None
246
+ # Tải lên tệp video
247
+ uploaded_file = st.file_uploader("Chọn một tệp video", type=['mp4', 'avi', 'mov'])
248
+
249
+ if uploaded_file is not None:
250
+ # Lưu tệp đã tải lên vào thư mục tạm thời
 
 
 
 
 
 
 
 
 
 
 
 
 
251
  temp_file = tempfile.NamedTemporaryFile(delete=False, suffix='.mp4')
252
+ temp_file.write(uploaded_file.read())
253
  video_path = temp_file.name
254
  temp_file.close()
255
 
 
256
  st.video(video_path)
 
 
257
 
258
+ # Hiển thị thông tin về hình
259
+ st.sidebar.header("Thông tin mô hình")
260
+ st.sidebar.markdown("""
261
+ - Phát hiện người: YOLOv8n
262
+ - Ước tính độ sâu: GLPN-NYU từ HuggingFace
263
+ """)
264
 
265
+ # Thêm tùy chọn cho độ tin cậy phát hiện
266
+ confidence = st.sidebar.slider("Ngưỡng tin cậy", 0.0, 1.0, 0.5)
267
 
268
+ # Nút để bắt đầu xử
269
+ if video_path and st.button("Xử lý Video"):
270
+ with st.spinner("Đang xử video..."):
271
+ detection_video_path, depth_video_path = process_video(video_path)
 
272
 
273
+ # Hiển thị video đã xử
274
+ st.subheader("Video đã xử ")
275
 
276
+ col1, col2 = st.columns(2)
277
+ with col1:
278
+ st.video(detection_video_path)
279
+ st.download_button(
280
+ label="Tải xuống video phát hiện",
281
+ data=open(detection_video_path, 'rb').read(),
282
+ file_name="person_detection_with_depth.mp4",
283
+ mime="video/mp4"
284
+ )
 
 
285
 
286
+ with col2:
287
+ st.video(depth_video_path)
288
+ st.download_button(
289
+ label="Tải xuống bản đồ độ sâu",
290
+ data=open(depth_video_path, 'rb').read(),
291
+ file_name="depth_colormap.mp4",
292
+ mime="video/mp4"
293
+ )
294
 
295
+ # Xóa tệp tạm thời
296
+ os.unlink(video_path)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
297
 
298
+ # Tệp requirements.txt
299
  def create_requirements():
300
+ requirements = """
301
+ streamlit
302
+ numpy
303
+ Pillow
304
+ opencv-python
305
+ torch
306
+ torchvision
307
+ transformers
308
+ ultralytics
309
+ requests
310
+ opencv-python
311
+ """
312
+ return requirements
313
 
314
  if __name__ == "__main__":
315
  main()