Yolov8和Yolov10的差異以及後處理實現

重庆Debug發表於2024-06-07

Yolo模型可分為4個維度的概念

模型版本、資料集、模型變體(Variants)、動態/靜態模型。

Yolo各模型版本進展歷史

Yolov(2015年華盛頓大學的 Joseph Redmon 和 Ali Farhadi 釋出)
Yolov2(2016年Joseph Redmon釋出)
Yolov3(2018年Joseph Redmon釋出)
Yolov4(2020年Alexey Bochkovskiy釋出)
Yolov5(2018年Glen Jocher釋出)
Yolov6(2022年美團團隊釋出)
Yolov7(2022年WongKinYiu釋出)
Yolov8(2023年Ultralytics釋出)
Yolov9(2023年釋出)
Yolov10(2024年清華大學團隊釋出)
其中Yolov10是剛剛2024年5月底才剛釋出的,其中v10實現了一個無NMS的架構,具有一致的雙重分配,顯著減少了後處理時間,並改善了整體延遲,讓後處理變得更簡單很多。

資料集(用於訓練)

COCO(Common Objects in Context)

COCO旨在鼓勵對各種物件類別的研究,通常用於對計算機視覺模型進行基準測試。
COCO 包含 330K 張影像,其中 200K 張影像帶有物件檢測、分割和字幕任務的註釋。
該資料集僅包含 80 個物件類別,包括汽車、腳踏車和動物、雨傘、手提包和運動器材等常見物件。
用途主要是拿來比較各種AI模型的優劣的,不適合實際商業用途。
COCO資料集下載地址:https://cocodataset.org/#download

OIV7(Open Image V7 )

Open Image V7 是 Google 倡導的多功能、廣泛的資料集。它旨在推動計算機視覺領域的研究,擁有大量標註有大量資料的影像900萬張,在邊界框標註的 190 萬張影像中,支援涵蓋 600 個物件類別,包含的 1600 萬個邊界框。這些邊界框主要由專家手工繪製,確保高精度。資料集總體積有561GB。
資料集下載地址:https://storage.googleapis.com/openimages/web/download_v7.html

注:目前Yolov8有Open Image V7和COCO兩種資料集已經有別人訓練好了的權重檔案。
而Yolov10因為是剛出來只找到COCO一種資料集訓練好的權重檔案,也就是說Yolov10只能識別80種物體,除非我們自己去訓練。

模型變體(Variants)

下面只列出來我有嘗試過匯出了的:
YOLOv8-N / YOLOv10-N:適用於資源極其受限的環境的奈米版本。
YOLOv8-S / YOLOv10-S:平衡速度和準確度的小型版本。
YOLOv8-M / YOLOv10-M:適用於通用用途的中型版本。
YOLOv10-B:平衡版本,寬度增加,準確度更高。
YOLOv8-L / YOLOv10-L:大型版本,以增加計算資源為代價,實現更高的準確度。
YOLOv8-X / YOLOv10-X:超大型版本,可實現最大準確度和效能。
注:v10有6種,v8只有5種。
v8和v10的coco資料集分別佔體積大小見下截圖:

動態/靜態模型

模型支援匯出成靜態模型和動態模型,靜態模型是[1,3,640,640],要求寬高符合32對齊。
動態模型則沒有要求,其中v8的動態模型會隨著輸入尺寸不同,輸出的尺寸會跟著變化。
而v10輸入尺寸無論怎樣,輸出的尺寸都是固定的[1,300,6]。
我已將動態靜態兩種處理方式都融合在一份程式碼上,根據載入後的模型推理後的輸出長度是否等於1800來判斷是否是v10,均可在其內部進行處理。

Demo截圖

Yolov8的後處理程式碼:

std::vector<YoloResult> filterYolov8Detections(
    float* inputs, float confidence_threshold,
    int num_channels, int num_anchors, int num_labels,
    int infer_img_width, int infer_img_height
    )
{
    std::vector<YoloResult> detections;
    cv::Mat output =
        cv::Mat((int)num_channels, (int)num_anchors,
                CV_32F, inputs).t();

    for (int i = 0; i < num_anchors; i++) {
        auto  row_ptr    = output.row(i).ptr<float>();
        auto  bboxes_ptr = row_ptr;
        auto  scores_ptr = row_ptr + 4;
        auto  max_s_ptr  = std::max_element(scores_ptr, scores_ptr + num_labels);
        float score      = *max_s_ptr;
        if (score > confidence_threshold) {
            float x = *bboxes_ptr++;
            float y = *bboxes_ptr++;
            float w = *bboxes_ptr++;
            float h = *bboxes_ptr;

            float x0 = std::clamp((x - 0.5f * w), 0.f, (float)infer_img_width);
            float y0 = std::clamp((y - 0.5f * h), 0.f, (float)infer_img_height);
            float x1 = std::clamp((x + 0.5f * w), 0.f, (float)infer_img_width);
            float y1 = std::clamp((y + 0.5f * h), 0.f, (float)infer_img_height);

            cv::Rect_<float> bbox;
            bbox.x      = x0;
            bbox.y      = y0;
            bbox.width  = x1 - x0;
            bbox.height = y1 - y0;
            YoloResult object;
            object.object_id = max_s_ptr - scores_ptr;
            object.score = score;
            object.box = bbox;
            detections.emplace_back(object);
        }
    }
    return detections;
}

Yolov10的後處理程式碼:

std::vector<YoloResult> filterYolov10Detections(
    const std::vector<float> &inputs, float confidence_threshold)
{
    std::vector<YoloResult> detections;
    const int num_detections = inputs.size() / 6;
    for (int i = 0; i < num_detections; ++i)
    {
        float left = inputs[i * 6 + 0];
        float top = inputs[i * 6 + 1];
        float right = inputs[i * 6 + 2];
        float bottom = inputs[i * 6 + 3];
        float confidence = inputs[i * 6 + 4];
        int class_id = inputs[i * 6 + 5];

        if (confidence >= confidence_threshold)
        {
            cv::Rect_<float> bbox;
            bbox.x = left;
            bbox.y = top;
            bbox.width = right - left;
            bbox.height = bottom - top;
            detections.push_back({class_id, confidence, bbox});
        }
    }
    return detections;
}

相關文章