Triển khai Mạng Phát Hiện Đối Tượng OBB với YOLOv8 bằng OpenCV C++

Để triển khai mô hình phát hiện đối tượng xoay (Oriented Bounding Box – OBB) dựa trên kiến trúc YOLOv8 trong môi trường C++, chỉ sử dụng thư viện cv::dnn của OpenCV, bước đầu tiên là có được mô hình ở định dạng ONNX — có thể từ mô hình tiền huấn luyện chính thức hoặc từ phiên bản đã huấn luyện riêng.

Mô hình OBB thường nhận đầu vào dạng tensor kích thước 1×3×H×W (ví dụ: 1×3×640×640), và trả về đầu ra dạng ma trận kích thước 1×7×N, trong đó N là số lượng đề xuất (thường là 8400). Mỗi hàng tương ứng với một hộp xoay, gồm 7 giá trị theo thứ tự: tọa độ tâm (cx, cy), chiều rộng w, chiều cao h, góc xoay θ (đơn vị radian, nằm trong khoảng [−π/4, 3π/4]), và hai giá trị xác suất phân lớp (hoặc một giá trị confidence + chỉ số lớp tùy cấu trúc).

Sau khi tải mô hình và ảnh, ta chuẩn bị blob đầu vào:

cv::Mat inputImage = cv::imread("input.jpg");
cv::Mat normalizedBlob;
cv::dnn::blobFromImage(
    inputImage,
    normalizedBlob,
    1.0 / 255.0,
    cv::Size(640, 640),
    cv::Scalar(0, 0, 0),
    true,     // swapRB
    false     // crop
);

net.setInput(normalizedBlob);
std::vector inferenceOutputs;
net.forward(inferenceOutputs, net.getUnconnectedOutLayersNames());

cv::Mat detectionTensor = inferenceOutputs[0].reshape(1, inferenceOutputs[0].size[2]); // reshape to [N x 7]

Để quản lý kết quả một cách rõ ràng, định nghĩa cấu trúc dữ liệu phù hợp:

struct RotatedBoxResult {
    int labelId;
    std::string labelName;
    float score;
    cv::Scalar drawColor;
    cv::Point2f center;
    cv::Size2f size;
    float angleRad;
    cv::RotatedRect rotatedRect() const {
        return cv::RotatedRect(center, size, angleRad * 180.0f / CV_PI);
    }
};

Với mỗi dòng trong detectionTensor, trích xuất các thành phần tương ứng và suy ra nhãn lớp bằng cách tìm giá trị lớn nhất trong vector xác suất:

std::vector<RotatedBoxResult> results;
const float* rowPtr = detectionTensor.ptr<float>(0);

for (int i = 0; i < detectionTensor.rows; ++i) {
    const float* row = detectionTensor.ptr<float>(i);
    cv::Point2f center(row[0], row[1]);
    cv::Size2f dims(row[2], row[3]);
    float theta = row[4];
    float conf = row[5];
    int clsIdx = static_cast<int>(row[6]);

    if (conf < 0.3f) continue; // ngưỡng lọc

    RotatedBoxResult item;
    item.center = center;
    item.size = dims;
    item.angleRad = theta;
    item.score = conf;
    item.labelId = clsIdx;
    item.labelName = classNames.at(clsIdx);
    item.drawColor = cv::Scalar(
        (rand() % 156) + 100,
        (rand() % 156) + 100,
        (rand() % 156) + 100
    );
    results.push_back(item);
}

Cuối cùng, để trực quan hóa kết quả, duyệt qua results và vẽ từng cv::RotatedRect lên ảnh gốc bằng cv::polylines hoặc cv::drawContours sau khi chuyển đổi sang đa giác 4 đỉnh.

Thẻ: yolov8 opencv dnn obb cpp

Đăng vào ngày 11 tháng 10 lúc 01:16