Dự án: đếm người qua cửa với YOLO26 và tracking
Kết hợp phát hiện đối tượng và theo dõi để đếm lượt vào, ra theo hai chiều; đánh giá sai số và tăng tốc trên máy yếu.
Nội dung bài viết
Đếm người ra vào cửa hàng, đếm xe qua cổng, đếm sản phẩm đa dạng trên băng tải: tất cả đều là một bài toán gồm hai phần, phát hiện (detection) và theo dõi (tracking). Dự án này dùng YOLO26 của Ultralytics với tracker tích hợp sẵn để đếm người đi qua một vạch theo hai chiều vào/ra.
Vì sao cần tracking?
Detection chỉ cho biết "khung hình này có 3 người ở đâu". Để đếm, ta cần biết người ở khung hình sau có phải là cùng một người ở khung hình trước không. Tracker gán cho mỗi đối tượng một ID ổn định qua các khung hình (Ultralytics mặc định dùng BoT-SORT, có thể đổi sang ByteTrack hoặc tracker khác qua file YAML).
Cài đặt
pip install ultralytics opencv-python
Mã nguồn
import cv2
from ultralytics import YOLO
model = YOLO("yolo26n.pt") # mô hình COCO, lớp 0 = person
cap = cv2.VideoCapture("cua-ra-vao.mp4")
LINE_Y = 360 # vạch đếm nằm ngang
last_y = {} # track_id -> tọa độ y của tâm ở khung trước
count_in, count_out = 0, 0
while True:
ok, frame = cap.read()
if not ok:
break
results = model.track(frame, persist=True, classes=[0], conf=0.4, verbose=False)
boxes = results[0].boxes
if boxes.id is not None:
ids = boxes.id.int().tolist()
for tid, (cx, cy, w, h) in zip(ids, boxes.xywh.tolist()):
prev = last_y.get(tid)
if prev is not None:
if prev < LINE_Y <= cy:
count_in += 1 # đi từ trên xuống: vào
elif prev >= LINE_Y > cy:
count_out += 1 # đi từ dưới lên: ra
last_y[tid] = cy
cv2.rectangle(frame, (int(cx - w / 2), int(cy - h / 2)),
(int(cx + w / 2), int(cy + h / 2)), (0, 255, 0), 2)
cv2.putText(frame, f"#{tid}", (int(cx - w / 2), int(cy - h / 2) - 6),
cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2)
cv2.line(frame, (0, LINE_Y), (frame.shape[1], LINE_Y), (0, 255, 255), 2)
cv2.putText(frame, f"Vao: {count_in} Ra: {count_out}", (10, 40),
cv2.FONT_HERSHEY_SIMPLEX, 1.1, (0, 0, 255), 3)
cv2.imshow("Dem nguoi", frame)
if cv2.waitKey(1) & 0xFF == ord("q"):
break
cap.release()
cv2.destroyAllWindows()
Điểm mấu chốt là persist=True: nó giữ trạng thái tracker giữa các lần gọi, nếu thiếu thì ID bị đặt lại mỗi khung hình. Ultralytics cũng có sẵn lớp solutions.ObjectCounter làm việc tương tự chỉ với vài dòng; tự viết như trên giúp bạn hiểu cơ chế và tùy biến logic đếm.
Đánh giá độ chính xác
- Quay 10 phút video ở vị trí thật, tự đếm tay số người vào/ra.
- Chạy chương trình, so sánh. Sai số tính theo % của tổng số lượt.
- Xem lại các đoạn đếm sai để tìm nguyên nhân.
| Lỗi | Nguyên nhân thường gặp | Cách khắc phục |
|---|---|---|
| Đếm thiếu khi đông người | Người che khuất nhau | Đặt camera cao, nhìn chéo xuống hoặc nhìn thẳng từ trên |
| Đếm 2 lần một người | Mất ID rồi gán ID mới khi đứng ngay vạch | Dùng "vùng đệm" hai vạch thay vì một vạch |
| Bỏ sót người ở xa | Người quá nhỏ trong ảnh | Tăng imgsz, dùng mô hình cỡ s/m |
Chạy nhanh hơn trên máy yếu
- Giảm độ phân giải đầu vào hoặc chỉ xử lý mỗi khung thứ 2.
- Xuất sang OpenVINO (CPU Intel) hoặc TensorRT (GPU NVIDIA, Jetson):
model.export(format="openvino"). - Chỉ chạy detection trong vùng quanh cửa (cắt ROI).
Quyền riêng tư
Hệ thống đếm người xử lý hình ảnh con người. Chỉ lưu số đếm, không lưu video nếu không cần; thông báo rõ khu vực có camera và tuân thủ quy định về bảo vệ dữ liệu cá nhân hiện hành.
Muốn huấn luyện YOLO cho vật thể của riêng bạn? Xem bài Object Detection với YOLO26.


