1. Mục tiêu
Tôi cần nhận dạng các vật thể căn bản sử dụng camera máy tính. Chương trình chạy viết bằng C++ để tối ưu tốc độ trên máy tính không có GPU. Số lượng vật thể cần nhận dạng khoảng 600 loại từ thư viện ảnh Open Images V7
Việc đầu tiên tôi nghĩ ngay đến là Yolo, thư viện nhận dạng rất nổi tiếng liên tục được nâng cấp phiên bản.
Yolo gần đây V8,9,11, 12 do Ultralytics ngôn ngữ lập trình giao tiếp là Python. Còn thư viện lõi bên trong là Pytorch. Pytorch được viết bằng C/C++
những phần quan trọng và sử dụng hầu hết các thư viện C++ tốc độ cao như C++ (ATen / LibTorch), CUDA (C++ + CUDA), Cython / Pybind11, Assembly. Như vậy Yolo có tốc độ xử lý rất tốt nếu chạy trên các thiết bị có GPU, thư viện CUDA xử lý ma trận song song.
Tuy nhiên khi bạn porting lên thiết bị di động không có GPU, và không thể cài Python thì việc dùng Yolo thuần sẽ có nhiều bất cập. Do đó chúng ta sẽ dùng một thư viện C/C++
khác để thực hiện suy luận dùng lại cấu trúc mạng neural và các tham số do Yolo đào tạo trước đó. Thư viện ncnn do Tencent phát triển được chọn vì nó viết bằng C/C++ tối ưu tốc độ cho các thiết bị chạy CPU Intel, ARM .
Tuy nhiên để ncnn chạy được, chúng cần chuyển đổi định dạng cấu trúc mạng neural và các tham số từ Yolo sang ncnn.
2. Một số khái niệm
🧠 YOLO (You Only Look Once)
Khái niệm | Giải thích đơn giản |
---|---|
Bounding Box | Hộp giới hạn đối tượng (tọa độ x, y, width, height). |
Confidence Score | Mức tin cậy mô hình cho rằng có đối tượng tại bounding box. |
Class Probability | Xác suất đối tượng thuộc một lớp cụ thể (xe, người, chó…). |
Anchor Box | Mẫu hộp để mô hình học cách xác định kích thước đối tượng. |
NMS (Non-Max Suppression) | Loại bỏ các bounding box bị trùng lặp, chỉ giữ lại cái tốt nhất. |
Input Size (imgsz) | Kích thước ảnh đầu vào cho model (ví dụ: 320x320). |
⚙️ ncnn (Inference Engine)
ncnn là một framework inference AI nhẹ (lightweight neural network inference framework) mã nguồn mở do Tencent phát triển, tối ưu để chạy mô hình deep learning trên thiết bị di động (Android, iOS), nhúng, và edge mà không cần GPU.
✅ Đặc điểm chính:
- Viết bằng C++, không phụ thuộc vào hệ thống runtime bên ngoài.
- Chạy tốt trên CPU ARM (và x86), đặc biệt tối ưu cho thiết bị di động.
- Không cần thư viện bên ngoài như OpenBLAS hay MKL.
- Tối ưu SIMD: hỗ trợ NEON (ARM), AVX (x86), và cả Vulkan (GPU nhẹ).
- Hỗ trợ nhiều kiến trúc mô hình phổ biến: MobileNet, YOLO, ResNet, ShuffleNet, EfficientNet, v.v.
- Có công cụ chuyển đổi từ PyTorch/ONNX sang NCNN (
onnx2ncnn
).
📦 Thành phần chính:
Thành phần | Vai trò |
---|---|
ncnn::Net | Mô hình đã load, dùng để chạy inference. |
ncnn::Mat | Lưu ảnh hoặc tensor đầu vào / đầu ra. |
Extractor | Công cụ lấy dữ liệu vào và trích xuất kết quả từ mô hình. |
.param + .bin | File cấu trúc mạng + trọng số đã được ncnn chuyển đổi. |
Khái niệm | Giải thích đơn giản |
---|---|
Param file (.param) | Mô tả kiến trúc mạng. |
Bin file (.bin) | Trọng số của mạng. |
Extractor | Thành phần dùng để truyền dữ liệu vào và lấy kết quả ra từ mô hình. |
ncnn::Mat | Kiểu dữ liệu lưu ảnh hoặc tensor cho NCNN. |
convert-to-ncnn | Quá trình chuyển model từ PyTorch/ONNX sang định dạng .param và .bin. |
set_num_threads(n) | Thiết lập số luồng xử lý để tăng tốc. |
🖼️ Open Images V7 (Dataset)
Khái niệm | Giải thích đơn giản |
---|---|
Class Label | Tên lớp đối tượng (ví dụ: Person , Car , Laptop …). |
BBox CSV | File chứa toạ độ các bounding box và tên lớp. |
Hierarchical Labels | Mỗi class có thể có class cha (ví dụ: Vehicle > Car ). |
ImageID | Mã định danh duy nhất cho mỗi ảnh trong tập dữ liệu. |
Train/Test Split | Bộ ảnh đã được phân tách để huấn luyện và kiểm thử. |
📌 Dữ liệu OpenImages chủ yếu dùng để huấn luyện model, không dùng trực tiếp trong nhận dạng.
🔧 OpenCV (Tiền xử lý & Hậu xử lý)
📌 Hàm tiền xử lý ảnh
Hàm | Mục đích |
---|---|
cv::imread(path) | Đọc ảnh từ file. |
cv::resize(img, size) | Thay đổi kích thước ảnh. |
cv::cvtColor(img, COLOR_BGR2RGB) | Chuyển ảnh từ BGR sang RGB (nếu cần). |
img.convertTo(img, CV_32F, 1/255.0) | Chuyển ảnh về float và chuẩn hóa 0-1. |
cv::Mat::ptr() | Truy cập dữ liệu ảnh để copy vào NCNN. |
📌 Hàm hậu xử lý (hiển thị và kết quả)
Hàm | Mục đích |
---|---|
cv::rectangle(img, rect, color) | Vẽ bounding box lên ảnh. |
cv::putText(img, label, point, font, size, color) | Ghi nhãn lớp lên ảnh. |
cv::imshow("result", img) | Hiển thị ảnh kết quả. |
cv::waitKey(0) | Đợi người dùng nhấn phím để đóng cửa sổ ảnh. |
✅ Quy trình nhận dạng cho người mới (Tóm tắt)
- Đọc ảnh bằng
cv::imread
. - Tiền xử lý ảnh: resize, chuyển màu, chuẩn hóa.
- Nạp ảnh vào
ncnn::Mat
, chạy model. - Nhận kết quả: bounding boxes, class, confidence.
- Áp dụng NMS, lọc kết quả tốt.
- Dùng OpenCV để vẽ kết quả lên ảnh và hiển thị.
3. Các bước cài đặt trên máy Mac M1 (Linux cũng gần tương tự)
3.1 Chuẩn bị môi trường Python
Vì ultralytics được viết bằng Python do đó cần cài đặt công cụ quản lý các thư viện và tạo môi trường Python là uv:
curl -LsSf https://astral.sh/uv/install.sh | sh
uv --version
Tạo môi trường ảo
uv venv
source .venv/bin/activate
3.2 Cài đặt thư viện ultralytics
Thư viện ultralytics dùng để chuyển đổi định dạng cấu trúc mạng, tham số từ Yolo sang ncnn.
uv pip install ncnn
uv pip install ultralytics
3.4 Tải model đã được huấn luyện sẵn
Tải model Google Open Images V7 Pretrained Models từ trang này https://docs.ultralytics.com/datasets/detect/open-images-v7/. Google Open Images V7 gồm 600 loại đồ vật đã được huấn luyện nhận dạng.
yolo export model=yolov8x-oiv7.pt format=ncnn imgsz=320 half=True
4. Lập trình C++
4.1 Cài bổ xung các thư viện C++ cần dùng
Cài thư viện opencv để lấy từng khung hình từ camera
brew install opencv
Cài thư viện ncnn viết bằng C++ dùng để nạp pretrained model được ra ở bước 3 rồi suy luận
brew install ncnn
thư viện glslang để tận dụng GPU trên máy thực hiện phép tính ma trận. Thư viện ncnn sẽ dùng glslang
brew install glslang
4.2 Lập trình ứng dụng C++
Mã nguồn dự án ở đây https://github.com/techmaster-vietnam/ObjectDetection
Đầu tiên mình phải nói về bản quyền tác giả code này. Mình giao cho một sinh viên thực tập code, bạn ý lên mạng tìm được đâu đó code, hoặc có thể hỏi AI để sinh mã. Ứng dụng chạy thử với model dataset Coco nhưng rất chậm. Mình tiếp nhận dự án với mục tiêu tăng tốc. Mình thực hiện một số cải tiến:
- Đọc danh sách các label từ file *.yaml
- Tăng tốc thực hiện nhận dạng từng khung hình. Mình phát hiện ra trong vòng lặp, code cũ liên tục đọc file model để nạp vào điều này tạo ra độ trễ rất lớn. Có lẽ code ban đầu chỉ là nhận dạng một file ảnh tĩnh xuất ra ảnh được vẽ các bounding box và label nhận dạng rồi ghi lên ảnh.
- Thay vì nhận dạng tất cả các vật thể thì chỉ tập trung nhận dạng vật thể gần tâm chính giữa màn hình nhất. Tại sao? Bởi dự án này chạy trên thiết bị di động, người dùng sẽ hướng camera về phía đối tượng cần nhận dạng cho đến khi khoanh vùng được vật thể mình quan tâm hơn là cố gắng nhận dạng 20 cái xe ô tô trong khung hình.
- Tiến tới loại bỏ thư viện OpenCV viết thay thế bằng những hàm tự viết để không cần phải cài đặt OpenCV vào thiết bị mobile. Ở dự án khác, bạn dev bên mình đã làm được.
4.2 Cách chạy thử mã nguồn của mình
Trong thư mục .vscode có file launch.json: Hãy chỉnh lại tham số args
"args": ["/Users/cuong/Desktop/oiv7"],
sang đường dẫn đến thư mục chứa model Yolo đã được huấn luyện. Thư mục này cần có những file sau:
- metadata.yaml
- model_ncnn.py
- model.ncnn.bin
- model.ncnn.param
4.3 Kết quả chạy thử
Mình chỉ vẽ những hình này sau khi ứng dụng nhận dạng được hầu hết các vật dung trong phòng làm việc của mình.
Nhận dạng nhầm cặp môi thành chim. Một phần vì ít khi ảnh huấn luyện chỉ chụp môi riêng lẻ
5. Kết luận
Thư viện ncnn kết hợp với Yolo nhận dạng vật thể rất nhanh, với 600 loại vật thể cũng không vấn đề gì.
Bình luận