Nếu như OpenCV truyền thống cần chúng ta tự chỉnh màu HSV, còn MediaPipe chuyên biệt cho bàn tay và khuôn mặt, thì YOLO (You Only Look Once) là đỉnh cao của thị giác máy tính hiện đại: nó có thể quét toàn bộ khung hình chỉ trong một lượt và nhận diện chính xác hàng chục loại vật thể khác nhau cùng lúc (con người, điện thoại, máy tính, cốc nước, xe cộ, chó, mèo...).
Trước khi có YOLO, các thuật toán cũ phải quét từng vùng nhỏ trên ảnh hàng trăm lần để tìm vật thể (rất chậm, chỉ đạt vài khung hình/giây).
YOLO đúng như tên gọi của nó (You Only Look Once): Đưa toàn bộ bức ảnh qua mạng nơ-ron tích chập (CNN) đúng 1 lần duy nhất.
Mạng nơ-ron chia bức ảnh thành một lưới (grid), đồng thời dự đoán:
Bounding Box: Tọa độ khung chữ nhật $[x_1, y_1, x_2, y_2]$ ôm lấy vật thể.
Class: Vật thể đó là gì (ví dụ: person, cup, cell phone).
Confidence Score: Độ tin cậy/xác suất đoán đúng của AI (từ $0.0$ đến $1.0$).
Phiên bản tiền huấn luyện (Pre-trained) của YOLOv8 được học trên tập dữ liệu chuẩn COCO (Common Objects in Context) gồm 80 nhóm đồ vật phổ biến hằng ngày.
YOLOv8 có nhiều kích cỡ mô hình:
yolov8n.pt (Nano): Nhỏ nhất, nhẹ nhất (~6 MB), chạy cực kỳ mượt trên CPU của máy tính thông thường hoặc Raspberry Pi.
yolov8s.pt (Small), yolov8m.pt (Medium): Chính xác hơn nhưng nặng hơn, cần máy có card đồ họa (GPU).$\implies$ chúng ta sẽ chọn bản yolov8n.pt để đảm bảo tốc độ FPS cao nhất!
ultralyticsMở Terminal và cài đặt thư viện chính thức của hãng phát triển YOLO:
pip install ultralytics
(Lưu ý: Lần đầu tiên chạy code, thư viện sẽ tự động tải file trọng số nhẹ yolov8n.pt về máy tính trong vài giây).
Chúng ta sẽ đóng gói thành lớp YOLODetector và xây dựng ứng dụng hiển thị tên đồ vật, độ tin cậy và FPS thời gian thực.
Tạo file buoi11_yolo_detection.py:
import cv2
import time
from ultralytics import YOLO
class YOLODetector:
"""Lớp quản lý mô hình YOLOv8"""
def __init__(self, model_name="yolov8n.pt", conf_threshold=0.5):
# 1. Nạp mô hình nhẹ Nano
print(f"📦 Đang nạp mô hình {model_name}...")
self.model = YOLO(model_name)
self.conf_threshold = conf_threshold
# Lấy danh sách tên 80 lớp từ mô hình
self.classes = self.model.names
def detect(self, frame):
"""Dự đoán và trích xuất các vật thể tìm thấy trong frame"""
# verbose=False: Tắt in log dày đặc ra terminal của YOLO
results = self.model(frame, conf=self.conf_threshold, verbose=False)
detections = []
# results[0].boxes chứa danh sách các bounding boxes tìm được
for box in results[0].boxes:
# Tọa độ hộp [x1, y1, x2, y2]
x1, y1, x2, y2 = map(int, box.xyxy[0])
# Độ tự tin (0.0 -> 1.0)
conf = float(box.conf[0])
# ID nhóm và tên đồ vật
cls_id = int(box.cls[0])
label = self.classes[cls_id]
detections.append({
"box": (x1, y1, x2, y2),
"conf": conf,
"label": label
})
return detections
class ObjectDetectionApp:
def __init__(self, camera_index=0):
self.cap = cv2.VideoCapture(camera_index)
# Giảm độ phân giải về 640x480 để CPU tính toán real-time
self.cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
self.cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)
self.detector = YOLODetector(conf_threshold=0.55)
self.prev_time = time.time()
def run(self):
if not self.cap.isOpened():
print("❌ Không thể kết nối Camera.")
return
print("--- YOLOv8 OBJECT DETECTION ĐANG CHẠY ---")
print(" -> Thử giơ điện thoại, cốc nước, chai lọ, sách vở trước camera!")
print(" -> Nhấn [Q] để thoát")
while True:
ret, frame = self.cap.read()
if not ret:
break
# Lật gương cho tự nhiên
frame = cv2.flip(frame, 1)
# 1. Nhận diện đồ vật qua YOLO
detections = self.detector.detect(frame)
# 2. Vẽ các hộp bao và nhãn tên lên khung hình
for item in detections:
x1, y1, x2, y2 = item["box"]
conf = item["conf"]
label = item["label"]
# Đổi màu hộp: Người (person) màu xanh dương, đồ vật khác màu xanh lá
box_color = (255, 100, 0) if label == "person" else (0, 255, 0)
# Vẽ khung chữ nhật ôm vật thể
cv2.rectangle(frame, (x1, y1), (x2, y2), box_color, 2)
# Vẽ nền nhãn chữ cho dễ đọc
text = f"{label} {int(conf * 100)}%"
(w, h), _ = cv2.getTextSize(text, cv2.FONT_HERSHEY_SIMPLEX, 0.6, 2)
cv2.rectangle(frame, (x1, y1 - 25), (x1 + w + 5, y1), box_color, -1)
cv2.putText(frame, text, (x1 + 2, y1 - 6),
cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 255, 255), 2)
# 3. Tính và hiển thị FPS
curr_time = time.time()
fps = 1 / (curr_time - self.prev_time) if (curr_time - self.prev_time) > 0 else 0
self.prev_time = curr_time
cv2.putText(frame, f"FPS: {int(fps)}", (20, 35),
cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 0, 255), 2)
cv2.putText(frame, f"Detected: {len(detections)}", (20, 65),
cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 255, 0), 2)
cv2.imshow("Buoi 11: YOLOv8 Real-Time Detection", frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
self.cap.release()
cv2.destroyAllWindows()
print(" Đã tắt chương trình an toàn.")
if __name__ == "__main__":
app = ObjectDetectionApp()
app.run()
Thực hành trải nghiệm:
Cho bé lấy các vật dụng quen thuộc quanh phòng giơ trước webcam:
Cầm điện thoại di động $\rightarrow$ Máy sẽ hiện cell phone.
Cầm cốc/ly uống nước $\rightarrow$ Hiện cup hoặc bottle.
Cầm một cuốn sách $\rightarrow$ Hiện book.
Bé đứng lùi ra xa hoặc bố bước vào khung hình $\rightarrow$ Máy nhận diện cùng lúc 2 người (person).
Thử thách tư duy cho bé:
Thử thách 1 (Bộ lọc đối tượng quan tâm - Class Filtering):
Làm thế nào để camera CHỈ báo động và vẽ khung khi nhìn thấy cell phone, còn mọi vật thể khác (người, bàn, ghế...) thì bỏ qua không vẽ? (Gợi ý: Thêm điều kiện if label == "cell phone":).
Thử thách 2 (Ứng dụng chống xao nhãng khi học bài):
Kết hợp logic: Nếu phát hiện cell phone xuất hiện liên tục trong khung hình quá 3 giây $\implies$ Bật cảnh báo: "Hãy cất điện thoại đi và tập trung học bài!".