當前位置: 妍妍網 > 碼農

YOLOv10在CPU上也能實作50+FPS推理—使用OpenVINO C++部署YOLOv10

2024-06-28碼農

點選藍字 關註我

英特爾發行版 OpenVINO™ 工具套件基於 oneAPI 而開發,可以加快高效能電腦視覺和深度學習視覺套用開發速度工具套件,適用於從邊緣到雲的各種英特爾平台上,幫助使用者更快地將更準確的真實世界結果部署到生產系統中。YOLOv10是清華大學研究人員近期提出的一種即時目標檢測方法,透過消除NMS、最佳化模型架構和引入創新模組等策略,在保持高精度的同時顯著降低了計算開銷,為即時目標檢測領域帶來了新的突破。

在本文中,我們將演示如何使用Intel OpenVINO™ C++ API 部署YOLOv10目標檢測模型,並使用 OpenVINO™ 異步推理介面實作模型推理加速。下面看一下YOLOv10模型在OpenVINO™上的執行效果吧:

1. 前言

英特爾發行版 OpenVINO™ 工具套件基於 oneAPI 而開發,可以加快高效能電腦視覺和深度學習視覺套用開發速度工具套件,適用於從邊緣到雲的各種英特爾平台上,幫助使用者更快地將更準確的真實世界結果部署到生產系統中。透過簡化的開發工作流程,OpenVINO™ 可賦能開發者在現實世界中部署高效能應用程式和演算法。

03b06c0aec22e58235cd01fa6d9b6db4

2024年4月25日,英特爾釋出了開源 OpenVINO™ 2024.1 工具包,用於在各種硬體上最佳化和部署人工智慧推理。更新了更多的 Gen AI 覆蓋範圍和框架整合,以最大限度地減少程式碼更改。同時提供了更廣泛的 LLM 模型支持和更多的模型壓縮技術。透過壓縮嵌入的額外最佳化減少了 LLM 編譯時間,改進了采用英特爾®高級矩陣擴充套件 (Intel® AMX) 的第 4 代和第 5 代英特爾®至強®處理器上 LLM 的第 1 令牌效能。透過對英特爾®銳炫™ GPU 的 oneDNN、INT4 和 INT8 支持,實作更好的 LLM 壓縮和改進的效能。最後實作了更高的可移植性和效能,可在邊緣、雲端或本地執行 AI。

YOLOv10是清華大學研究人員近期提出的一種即時目標檢測方法,該方法在Ultralytics Python包的基礎上進行了多項創新和改進,主要有以下特點

  1. 消除非極大值抑制(NMS) :YOLOv10透過引入一致的雙重分配策略,在訓練時使用一對多的標簽分配來提供豐富的監督訊號,在推理時使用一對一的匹配,從而消除了對NMS的依賴。這一改進在保持高精度的同時,減少了推理延遲和計算量。

  2. 全面最佳化的模型架構 :YOLOv10從推理效率和準確性的角度出發,全面最佳化了模型的各個組成部份。這包括采用輕量級分類頭、空間通道去耦下采樣和等級引導塊設計等,以減少計算冗余並提高模型效能。

  3. 引入大核摺積和部份自註意模組 :為了提高效能,YOLOv10在不增加大量計算成本的前提下,引入了大核摺積和部份自註意模組。

  4. 多種模型尺寸可選 :官方釋出了從N到X各種型號的模型,以滿足不同套用的需求。這些模型包括超小型版本YOLOv10-N(用於資源極其有限環境)、小型版本YOLOv10-S(兼顧速度和精度)、中型版本YOLOv10-M(通用)、平衡型版本YOLOv10-B(寬度增加,精度更高)、大型版本YOLOv10-L(精度更高,但計算資源增加)以及超大型版本YOLOv10-X(可實作最高的精度和效能)。

透過廣泛的實驗驗證,YOLOv10在多個模型尺度上實作了卓越的精度-延遲權衡。例如,在COCO數據集上,YOLOv10-S在相似精度下比其他即時目標檢測方法更快,同時參數和浮點運算量也大幅減少。綜上所述,YOLOv10透過消除NMS、最佳化模型架構和引入創新模組等策略,在保持高精度的同時顯著降低了計算開銷,為即時目標檢測領域帶來了新的突破。

image-20240604135821296

2. 計畫開發環境

下面簡單介紹一下計畫的開發環境,開發者可以根據自己的裝置情況進行配置:

  • 系統平台:Windows 11

  • Intel Core i7-1165G7

  • 開發平台:Visual Studio 2022

  • OpenVINO™:2024.1.0

  • OpenCV:4.8.0

  • 此處程式碼開發平台使用的是C++,因此在計畫配置時,需要配置第三方依賴庫,分別是 OpenVINO™和OpenCV兩個個依賴庫,其配置方式此處不做詳述。

    3. 模型獲取與INT8量化

    為了提升模型的推理速度,我們此處使用 OpenVINO™ 進行推理加速,並使用OpenVINO™NNCF 工具對模型進行一個INT8量化。量化的詳細流程可以參考下面notebooks,該notebooks記錄了YOLOv10使用OpenVINO™量化的詳細流程,連結如下所示:

    https://github.com/openvinotoolkit/openvino_notebooks/tree/latest/notebooks/yolov10-optimization

    模型量化完成後,我們對比了一下量化前後模型變化,如下圖所示:

    圖片1

    4. 定義YOLOv10 Process

    4.1 數據預處理

    數據預處理此處透過OpenCV實作,將輸入的圖片數據轉為模型需要的數據情況,程式碼如下所示:

    voidpre_process(cv::Mat* img, int length, float* factor, std::vector<float>& data){
    cv::Mat mat;
    int rh = img->rows;
    int rw = img->cols;
    int rc = img->channels();
    cv::cvtColor(*img, mat, cv::COLOR_BGR2RGB);
    int max_image_length = rw > rh ? rw : rh;
    cv::Mat max_image = cv::Mat::zeros(max_image_length, max_image_length, CV_8UC3);
    max_image = max_image * 255;
    cv::Rect roi(00, rw, rh);
    mat.copyTo(cv::Mat(max_image, roi));
    cv::Mat resize_img;
    cv::resize(max_image, resize_img, cv::Size(length, length), 0.0f0.0f, cv::INTER_LINEAR);
    *factor = (float)((float)max_image_length / (float)length);
    resize_img.convertTo(resize_img, CV_32FC3, 1 / 255.0);
    rh = resize_img.rows;
    rw = resize_img.cols;
    rc = resize_img.channels();
    for (int i = 0; i < rc; ++i) {
    cv::extractChannel(resize_img, cv::Mat(rh, rw, CV_32FC1, data.data() + i * rh * rw), i);
    }
    }

    在呼叫時也相對簡單,將相關變量傳入即可,程式碼如下所示:

    Mat frame = new frame();
    std::vector<floatinput_data(640 * 640 * 3);
    float factor = 0;
    pre_process(&frame, 640, &factor, input_data);

    4.2 結果後處理

    首先此處定義了一個結果類:

    structDetResult {
    cv::Rect bbox;
    float conf;
    int lable;
    DetResult(cv::Rect bbox,float conf,int lable):bbox(bbox),conf(conf),lable(lable){}
    };

    然後定義模型的結果處理方式,程式碼如下所示:

    std::vector<DetResult> post_process(float* result, float factor, int outputLength){
    std::vector<cv::Rect> position_boxes;
    std::vector <int> class_ids;
    std::vector <float> confidences;
    // Preprocessing output results
    for (int i = 0; i < outputLength; i++)
    {
    int s = 6 * i;
    if ((float)result[s + 4] > 0.2)
    {
    float cx = result[s + 0];
    float cy = result[s + 1];
    float dx = result[s + 2];
    float dy = result[s + 3];
    int x = (int)((cx)*factor);
    int y = (int)((cy)*factor);
    int width = (int)((dx - cx) * factor);
    int height = (int)((dy - cy) * factor);
    cv::Rect box(x, y, width, height);
    position_boxes.push_back(box);
    class_ids.push_back((int)result[s + 5]);
    confidences.push_back((float)result[s + 4]);
    }
    }
    std::vector<DetResult> re;
    for (int i = 0; i < position_boxes.size(); i++)
    {
    DetResult det(position_boxes[i], confidences[i], class_ids[i]);
    re.push_back(det);
    }
    return re;
    }

    最後為了讓結果視覺化,定義了結果繪制方法,程式碼如下所示:

    voiddraw_bbox(cv::Mat& img, std::vector<DetResult>& res){
    for (size_t j = 0; j < res.size(); j++) {
    cv::rectangle(img, res[j].bbox, cv::Scalar(2550255), 2);
    cv::putText(img, std::to_string(res[j].lable) + "-" + std::to_string(res[j].conf),
    cv::Point(res[j].bbox.x, res[j].bbox.y - 1), cv::FONT_HERSHEY_PLAIN,
    1.2, cv::Scalar(00255), 2);
    }
    }

    上述方式呼叫依舊十分容易,使用程式碼如下所示:

    std::vector<floatoutput_data(300 * 6);
    std::vector<DetResult> result = post_process(output_data.data(), factor, 300);
    draw_bbox(frame, result);

    5. 模型推理實作

    5.1 基本推理實作

    首先實作一下常規的同步推理程式碼,如下面所示:

    voidyolov10_infer_without_process(){
    std::string videoPath = "E:\\Text_dataset\\car_test.mov";
    std::string model_path = "E:\\Text_Model\\yolov10s_openvino_model\\yolov10s.xml";
    ov::Core core;
    auto model = core.read_model(model_path);
    auto compiled_model = core.compile_model(model, "CPU");
    ov::InferRequest request =compiled_model.create_infer_request();
    cv::VideoCapture capture(videoPath);
    if (!capture.isOpened()) {
    std::cerr << "ERROR: 視訊無法開啟" << std::endl;
    return;
    }
    float factor = 0;
    request.get_input_tensor().set_shape(std::vector<size_t>{13640640});
    std::vector<floatinputData(640 * 640 * 3);
    std::chrono::time_point<std::chrono::steady_clock> t_beg;
    std::chrono::time_point<std::chrono::steady_clock> t_end;
    while (true)
    {
    cv::Mat frame;
    if (!capture.read(frame)) {
    break;
    }
    t_beg = std::chrono::high_resolution_clock::now();
    pre_process(&frame, 640, &factor, inputData);
    memcpy(request.get_input_tensor().data<float>(), inputData.data(), 640 * 640 * 3);
    request.infer();
    float* output_data = request.get_output_tensor().data<float>();
    std::vector<DetResult> result = post_process(output_data, factor, 300);
    t_end = std::chrono::high_resolution_clock::now();
    cv::putText(frame, "FPS: " + std::to_string(1000.0 / std::chrono::duration<floatstd::milli>(t_end - t_beg).count()) 
    ", Time: " + std::to_string(std::chrono::duration<floatstd::milli>(t_end - t_beg).count()) + "ms",
    cv::Point(2040), 12, cv::Scalar(2550255), 2);
    draw_bbox(frame, result);
    imshow("讀取視訊", frame);
    cv::waitKey(1); //延時30
    }
    cv::destroyAllWindows();
    return;
    }

    5.2 使用異步推理實作

    視訊一般1s中有25幀左右,這就意味著我們需要1s推理25張圖片才可以實作視訊推理。一般情況下,在CPU裝置推理視覺模型很難實作即時推理,我們在使用 OpenVINO™推理時,經過一些最佳化技術後,勉強可以實作25FPS的推理,但是如果需要處理其他業務將會很難實作。因此為了提升推理速度,我們采用異步推理技術,實作程式碼如下所示:

    voidyolov10_infer_ansy_without_process(){
    std::string videoPath = "E:\\Text_dataset\\car_test.mov";
    std::string model_path = "E:\\Text_Model\\yolov10s_openvino_model\\yolov10s.xml";
    ov::Core core;
    auto model = core.read_model(model_path);
    auto compiled_model = core.compile_model(model, "CPU");
    std::vector<ov::InferRequest>requests = { compiled_model.create_infer_request(), compiled_model.create_infer_request() };
    cv::VideoCapture capture(videoPath);
    // 檢查網路攝影機是否成功開啟
    if (!capture.isOpened()) {
    std::cerr << "ERROR: 視訊無法開啟" << std::endl;
    return;
    }
    float factor = 0;
    requests[0].get_input_tensor().set_shape(std::vector<size_t>{13640640});
    requests[1].get_input_tensor().set_shape(std::vector<size_t>{13640640});
    cv::Mat frame;
    capture.read(frame);
    std::vector<floatinputData(640 * 640 * 3);
    pre_process(&frame, 640, &factor, inputData);
    memcpy(requests[0].get_input_tensor().data<float>(), inputData.data(), 640 * 640 * 3);
    requests[0].start_async();
    std::chrono::time_point<std::chrono::steady_clock> t_beg;
    std::chrono::time_point<std::chrono::steady_clock> t_end;
    while (true)
    {
    cv::Mat next_frame;
    if (!capture.read(next_frame)) {
    break;
    }
    t_beg = std::chrono::high_resolution_clock::now();
    pre_process(&next_frame, 640, &factor, inputData);
    memcpy(requests[1].get_input_tensor().data<float>(), inputData.data(), 640 * 640 * 3);
    requests[1].start_async();
    requests[0].wait();
    float* output_data = requests[0].get_output_tensor().data<float>();
    std::vector<DetResult> result = post_process(output_data, factor, 300);
    t_end = std::chrono::high_resolution_clock::now();
    draw_bbox(frame, result);
    cv::putText(frame, "FPS: " + std::to_string(1000.0 / std::chrono::duration<floatstd::milli>(t_end - t_beg).count())
    ", Time: " + std::to_string(std::chrono::duration<floatstd::milli>(t_end - t_beg).count()) + "ms",
    cv::Point(2040), 12, cv::Scalar(2550255), 2);
    imshow("讀取視訊", frame);
    cv::waitKey(1); //延時30
    frame = next_frame;
    std::swap(requests[0], requests[1]);
    }
    cv::destroyAllWindows();
    return;
    }

    文章中已經提供了全部程式碼檔以及模型的獲取方式,如果您還有任何疑問,可以下載現成的檔資源。為了方便開發者使用,我們將 程式碼、測試視訊與模型檔以及計畫所需依賴 打包,釋出到了CSDN上,大家可以根據自己需求進行下載,下載連結:

    https://download.csdn.net/download/Grape_yan/89473342

    6. 時間測試

    最後我們對推理時間進行了測試,分別測試了量化前後模型在同步推理以及異步推理的表現,如下表所示:

    Model API PrePocess Inference PostProcess Total FPS
    Float 32 Sync 9.72 ms 65.44 ms 0 ms 75.16 ms 13.30
    Float 32 Async 15.29 ms 40.52 ms 0 ms 55.81 ms 17.92
    INT 8 Sync 15.25 ms 25.64 ms 0 ms 43.89 ms 22.78
    INT 8 Async 17.84 ms 1.86 ms 0 ms 19.70 ms 50.76

    透過該表可以看出,量化前後,模型推理速度提升了1.5倍,並且透過使用異步推理介面,經過IN8量化後的模型,在本裝置上可以實作50FPS的推理速度,與為量化的模型相比,速度提升了2.8倍。透過異步介面,我們可以在CPU下輕松的實作視訊推理。

    7. 總結

    在本文中,我們演示了如何使用Intel OpenVINO™ C++ API 部署YOLOv10目標檢測模型,並使用 OpenVINO™ 異步推理介面實作模型推理加速。 最後如果各位開發者在使用中有任何問題,歡迎大家與我聯系。

    個人帳號 - 2

    點選藍字 關註我們