作者:顏國(guó)進(jìn)英特爾邊緣計(jì)算創(chuàng)新大使
RT-DETR 是在 DETR 模型基礎(chǔ)上進(jìn)行改進(jìn)的,一種基于DETR架構(gòu)的實(shí)時(shí)端到端檢測(cè)器,它通過使用一系列新的技術(shù)和算法,實(shí)現(xiàn)了更高效的訓(xùn)練和推理,在前文我們發(fā)表了《基于OpenVINOPython API部署RT-DETR模型|開發(fā)者實(shí)戰(zhàn)》,在該文章中,我們基于 OpenVINOPython API向大家展示了包含后處理的 RT-DETR 模型的部署流程,但在實(shí)際工業(yè)應(yīng)用中,我們?yōu)榱伺c當(dāng)前軟件平臺(tái)集成更多會(huì)采用 C++ 平臺(tái),因此在本文中,我們將基于 OpenVINOC++ API向大家展示了不包含后處理的 RT-DETR 模型的部署流程,并向大家展示如何導(dǎo)出不包含后處理的 RT-DETR 模型。
該項(xiàng)目所使用的全部代碼已經(jīng)在 GitHub 上開源,并且收藏在 OpenVINO-CSharp-API 項(xiàng)目里。
1. RT-DETR
飛槳在去年3月份推出了高精度通用目標(biāo)檢測(cè)模型PP-YOLOE,同年在PP-YOLOE的基礎(chǔ)上提出了PP-YOLOE+。而繼 PP-YOLOE提出后,MT-YOLOv6、YOLOv7、DAMO-YOLO、RTMDet等模型先后被提出,一直迭代到今年開年的YOLOv8。

YOLO檢測(cè)器有個(gè)較大的待改進(jìn)點(diǎn)是需要NMS后處理,其通常難以優(yōu)化且不夠魯棒,因此檢測(cè)器的速度存在延遲。DETR 是一種不需要NMS后處理、基于Transformer的端到端目標(biāo)檢測(cè)器。百度飛槳正式推出了——RT-DETR (Real-Time DEtection TRansformer),一種基于DETR架構(gòu)的實(shí)時(shí)端到端檢測(cè)器,其在速度和精度上取得了SOTA性能。

RT-DETR 是在 DETR 模型基礎(chǔ)上進(jìn)行改進(jìn)的,它通過使用一系列新的技術(shù)和算法,實(shí)現(xiàn)了更高效的訓(xùn)練和推理。具體來說,RT-DETR 具有以下優(yōu)勢(shì):
1. 實(shí)時(shí)性能更佳
RT-DETR 采用了一種新的注意力機(jī)制,能夠更好地捕獲物體之間的關(guān)系,并減少計(jì)算量。此外,RT-DETR 還引入了一種基于時(shí)間的注意力機(jī)制,能夠更好地處理視頻數(shù)據(jù)。
2. 精度更高
RT-DETR 在保證實(shí)時(shí)性能的同時(shí),還能夠保持較高的檢測(cè)精度。這主要得益于 RT-DETR 引入的一種新的多任務(wù)學(xué)習(xí)機(jī)制,能夠更好地利用訓(xùn)練數(shù)據(jù)。
3. 更易于訓(xùn)練和調(diào)參
RT-DETR 采用了一種新的損失函數(shù),能夠更好地進(jìn)行訓(xùn)練和調(diào)參。此外,RT-DETR 還引入了一種新的數(shù)據(jù)增強(qiáng)技術(shù),能夠更好地利用訓(xùn)練數(shù)據(jù)。

2. OpenVINO
英特爾發(fā)行版OpenVINO 工具套件基于 oneAPI而開發(fā),可以加快高性能計(jì)算機(jī)視覺和深度學(xué)習(xí)視覺應(yīng)用開發(fā)速度工具套件,適用于從邊緣到云的各種英特爾平臺(tái)上,幫助用戶更快地將更準(zhǔn)確的真實(shí)世界結(jié)果部署到生產(chǎn)系統(tǒng)中。通過簡(jiǎn)化的開發(fā)工作流程,OpenVINO 可賦能開發(fā)者在現(xiàn)實(shí)世界中部署高性能應(yīng)用程序和算法。

OpenVINO 2023.1 于 2023 年 9 月 18 日發(fā)布,該工具包帶來了挖掘生成人工智能全部潛力的新功能。生成人工智能的覆蓋范圍得到了擴(kuò)展,通過 PyTorch* 等框架增強(qiáng)了體驗(yàn),您可以在其中自動(dòng)導(dǎo)入和轉(zhuǎn)換模型。大型語言模型(LLM)在運(yùn)行時(shí)性能和內(nèi)存優(yōu)化方面得到了提升。聊天機(jī)器人、代碼生成等的模型已啟用。OpenVINO更便攜,性能更高,可以在任何需要的地方運(yùn)行:在邊緣、云中或本地。
3. 環(huán)境配置
在上一篇文章中我們以已經(jīng)向大家提供了 RT-DETR 模型導(dǎo)出蘇需要的環(huán)境,此處不再多做展示,為了大家更好的復(fù)現(xiàn)該項(xiàng)目代碼,此處向大家提供本次開發(fā)所使用的 C++ 環(huán)境:
openvino: 2023.1.0 opencv: 4.5.5
大家在復(fù)現(xiàn)代碼時(shí)可以使用相同的環(huán)境或者與作者所使用環(huán)境發(fā)布較為接近的環(huán)境進(jìn)行開發(fā),防止使用時(shí)出現(xiàn)不必要的錯(cuò)誤;此外該項(xiàng)目提供了兩種編譯方式,大家可以使用 Visual Studio 進(jìn)行編譯以及 CMake 進(jìn)行編譯。
4. 模型下載與轉(zhuǎn)換
在上一篇文章中我們已經(jīng)向大家展示了 RT-DETR 預(yù)訓(xùn)練模型的導(dǎo)出方式,該模型是默認(rèn)包含后處理的;因此在本文中,我們將向大家展示不包含后處理的 RT-DETR 模型導(dǎo)出方式以及兩種模型的差異。
4.1 模型導(dǎo)出
PaddleDetection 官方庫向我們提供了十分友好 API 接口,因此導(dǎo)出不包含后處理的 RT-DETR 模型也是十分容易的。首先修改配置文件,主要是修改 RT-DETR 模型的配置文件,配置文件路徑為:
.PaddleDetectionconfigs tdetr\_base_ tdetr_r50vd.yml
在配置文件 DETR 項(xiàng)目下增加 exclude_post_process: True 語句,如下圖所示:

然后重新運(yùn)行模型導(dǎo)出指令,便可以獲取不包含后處理的模型:
python tools/export_model.py -c configs/rtdetr/rtdetr_r50vd_6x_coco.yml -o weights=https://bj.bcebos.com/v1/paddledet/models/rtdetr_r50vd_6x_coco.pdparams trt=True --output_dir=output_inference
左滑查看更多
在模型導(dǎo)出后,我們可以轉(zhuǎn)換成 ONNX 格式以及 IR 格式,可參考上一篇文章中的模型轉(zhuǎn)換內(nèi)容。
4.2 模型信息對(duì)比
通過下表我們可以看出,裁剪后的模型,只包含一個(gè)輸入節(jié)點(diǎn),其輸出節(jié)點(diǎn)也發(fā)生了變化,原本模型輸出為處理后的預(yù)測(cè)輸出,經(jīng)過裁剪后,模型輸出節(jié)點(diǎn)輸出內(nèi)容發(fā)生了較大變化。其中:
stack_7.tmp_0.slice_0:該節(jié)點(diǎn)表示 300 種預(yù)測(cè)結(jié)果的預(yù)測(cè)框信息;
stack_8.tmp_0.slice_0:該節(jié)點(diǎn)表示 300 種預(yù)測(cè)結(jié)果的 80 種分類信息置信度,后續(xù)再處理時(shí),需要根據(jù)預(yù)測(cè)結(jié)果獲取最終的預(yù)測(cè)分類信息。



模型節(jié)點(diǎn)信息圖(左圖為包含后處理的模型節(jié)點(diǎn)信息,右圖為不包含后處理的模型節(jié)點(diǎn)信息)
5. C++ 代碼實(shí)現(xiàn)
為了更系統(tǒng)地實(shí)現(xiàn) RT-DETR 模型的推理流程,我們采用 C++ 特性,封裝了 RTDETRPredictor 模型推理類以及 RTDETRProcess 模型數(shù)據(jù)處理類,下面我們將對(duì)這兩個(gè)類中的關(guān)鍵代碼進(jìn)行講解。
5.1 模型推理類實(shí)現(xiàn)
C++ 代碼中我們定義的 RTDETRPredictor 模型推理類如下所示:
class RTDETRPredictor
{
public:
RTDETRPredictor(std::string model_path, std::string label_path,
std::string device_name = "CPU", bool postprcoess = true);
cv::Mat predict(cv::Mat image);
private:
void pritf_model_info(std::shared_ptr model);
void fill_tensor_data_image(ov::Tensor& input_tensor, const cv::Mat& input_image);
void fill_tensor_data_float(ov::Tensor& input_tensor, float* input_data, int data_size);
private:
RTDETRProcess rtdetr_process;
bool post_flag;
ov::Core core;
std::shared_ptr model;
ov::CompiledModel compiled_model;
ov::InferRequest infer_request;
};
左滑查看更多
1)模型推理類初始化
首先我們需要初始化模型推理類,初始化相關(guān)信息:
RTDETRPredictor::RTDETRPredictor(std::string model_path, std::string label_path,
std::string device_name, bool post_flag)
:post_flag(post_flag){
INFO("Model path: " + model_path);
INFO("Device name: " + device_name);
model = core.read_model(model_path);
pritf_model_info(model);
compiled_model = core.compile_model(model, device_name);
infer_request = compiled_model.create_infer_request();
rtdetr_process = RTDETRProcess(cv::Size(640, 640), label_path, 0.5);
}
左滑查看更多
在該方法中主要包含以下幾個(gè)輸入:
model_path:推理模型地址;
label_path:模型預(yù)測(cè)類別文件;
device_name:推理設(shè)備名稱;
post_flag:模型是否包含后處理,當(dāng) post_flag = true 時(shí),包含后處理,當(dāng) post_flag = false 時(shí),不包含后處理。
2)圖片預(yù)測(cè) API
這一步中主要是對(duì)輸入圖片進(jìn)行預(yù)測(cè),并將模型預(yù)測(cè)結(jié)果會(huì)知道輸入圖片上,下面是這階段的主要代碼:
cv::Mat RTDETRPredictor::predict(cv::Mat image){
cv::Mat blob_image = rtdetr_process.preprocess(image);
if (post_flag) {
ov::Tensor image_tensor = infer_request.get_tensor("image");
ov::Tensor shape_tensor = infer_request.get_tensor("im_shape");
ov::Tensor scale_tensor = infer_request.get_tensor("scale_factor");
image_tensor.set_shape({ 1,3,640,640 });
shape_tensor.set_shape({ 1,2 });
scale_tensor.set_shape({ 1,2 });
fill_tensor_data_image(image_tensor, blob_image);
fill_tensor_data_float(shape_tensor, rtdetr_process.get_input_shape().data(), 2);
fill_tensor_data_float(scale_tensor, rtdetr_process.get_scale_factor().data(), 2);
} else {
ov::Tensor image_tensor = infer_request.get_input_tensor();
fill_tensor_data_image(image_tensor, blob_image);
}
infer_request.infer();
ResultData results;
if (post_flag) {
ov::Tensor output_tensor = infer_request.get_tensor("reshape2_95.tmp_0");
float result[6 * 300] = {0};
for (int i = 0; i < 6 * 300; ++i) {
? ? ? ? ? ?result[i] = output_tensor.data()[i];
}
results = rtdetr_process.postprocess(result, nullptr, true);
} else {
ov::Tensor score_tensor = infer_request.get_tensor(model->outputs()[1].get_any_name());
ov::Tensor bbox_tensor = infer_request.get_tensor(model->outputs()[0].get_any_name());
float score[300 * 80] = {0};
float bbox[300 * 4] = {0};
for (int i = 0; i < 300; ++i) {
? ? ? ? ? ?for (int j = 0; j < 80; ++j) {
? ? ? ? ? ? ? ?score[80 * i + j] = score_tensor.data()[80 * i + j];
}
for (int j = 0; j < 4; ++j) {
? ? ? ? ? ? ? ?bbox[4 * i + j] = bbox_tensor.data()[4 * i + j];
}
}
results = rtdetr_process.postprocess(score, bbox, false);
}
return rtdetr_process.draw_box(image, results);
}
左滑查看更多
上述代碼的主要邏輯如下:首先是處理輸入圖片,調(diào)用定義的數(shù)據(jù)處理類,將輸入圖片處理成指定的數(shù)據(jù)類型;然后根據(jù)模型的輸入節(jié)點(diǎn)情況配置模型輸入數(shù)據(jù),如果使用的是動(dòng)態(tài)模型輸入,需要設(shè)置輸入形狀;接下來就是進(jìn)行模型推理;最后就是對(duì)推理結(jié)果進(jìn)行處理,并將結(jié)果繪制到輸入圖片上。
5.2 模型數(shù)據(jù)處理類 RTDETRProcess
1)定義 RTDETRProcess
class RTDETRProcess
{
public:
RTDETRProcess() {}
RTDETRProcess(cv::Size target_size, std::string label_path = NULL, float threshold = 0.5,
cv::InterpolationFlags interpf = cv::INTER_LINEAR);
cv::Mat preprocess(cv::Mat image);
ResultData postprocess(float* score, float* bboxs, bool post_flag);
std::vector get_im_shape() { return im_shape; }
std::vector get_input_shape() { return { (float)target_size.width ,(float)target_size.height }; }
std::vector get_scale_factor() { return scale_factor; }
cv::Mat draw_box(cv::Mat image, ResultData results);
private:
void read_labels(std::string label_path);
template
float sigmoid(T data) { return 1.0f / (1 + std::exp(-data));}
template
int argmax(T* data, int length) {
std::vector arr(data, data + length);
return (int)(std::max_element(arr.begin(), arr.end()) - arr.begin());
}
private:
cv::Size target_size; // The model input size.
std::vector labels; // The model classification label.
float threshold; // The threshold parameter.
cv::InterpolationFlags interpf; // The image scaling method.
std::vector im_shape;
std::vector scale_factor;
};
左滑查看更多
2) 輸入數(shù)據(jù)處理方法
cv::Mat RTDETRProcess::preprocess(cv::Mat image){
im_shape = { (float)image.rows, (float)image.cols };
scale_factor = { 640.0f / (float)image.rows, 640.0f / (float)image.cols};
cv::Mat blob_image;
cv::cvtColor(image, blob_image, cv::COLOR_BGR2RGB);
cv::resize(blob_image, blob_image, target_size, 0, 0, cv::INTER_LINEAR);
std::vector rgb_channels(3);
cv::split(blob_image, rgb_channels);
for (auto i = 0; i < rgb_channels.size(); i++) {
? ? ? ?rgb_channels[i].convertTo(rgb_channels[i], CV_32FC1, 1.0 / 255.0);
? ?}
? ?cv::merge(rgb_channels, blob_image);
? ?return blob_image;
}
左滑查看更多
3)預(yù)測(cè)結(jié)果數(shù)據(jù)處理方法
ResultData RTDETRProcess::postprocess(float* score, float* bbox, bool post_flag)
{
ResultData result;
if (post_flag) {
for (int i = 0; i < 300; ++i) {
? ? ? ? ? ?if (score[6 * i + 1] > threshold) {
result.clsids.push_back((int)score[6 * i ]);
result.labels.push_back(labels[(int)score[6 * i]]);
result.bboxs.push_back(cv::Rect(score[6 * i + 2], score[6 * i + 3],
score[6 * i + 4] - score[6 * i + 2],
score[6 * i + 5] - score[6 * i + 3]));
result.scores.push_back(score[6 * i + 1]);
}
}
} else {
for (int i = 0; i < 300; ++i) {
? ? ? ? ? ?float s[80];
? ? ? ? ? ?for (int j = 0; j < 80; ++j) {
? ? ? ? ? ? ? ?s[j] = score[80 * i + j];
? ? ? ? ? ?}
? ? ? ? ? ?int clsid = argmax(s, 80);
float max_score = sigmoid(s[clsid]);
if (max_score > threshold) {
result.clsids.push_back(clsid);
result.labels.push_back(labels[clsid]);
float cx = bbox[4 * i] * 640.0 / scale_factor[1];
float cy = bbox[4 * i + 1] * 640.0 / scale_factor[0];
float w = bbox[4 * i + 2] * 640.0 / scale_factor[1];
float h = bbox[4 * i + 3] * 640.0 / scale_factor[0];
result.bboxs.push_back(cv::Rect((int)(cx - w / 2), (int)(cy - h / 2), w, h));
result.scores.push_back(max_score);
}
}
}
return result;
}
左滑查看更多
此處對(duì)輸出結(jié)果做一個(gè)解釋,由于我們提供了兩種模型的輸出,此處提供了兩種模型的輸出數(shù)據(jù)處理方式,主要區(qū)別在于是否對(duì)預(yù)測(cè)框進(jìn)行還原以及對(duì)預(yù)測(cè)類別進(jìn)行提取,具體區(qū)別大家可以查看上述代碼。
6. 預(yù)測(cè)結(jié)果展示
最后通過上述代碼,我們最終可以直接實(shí)現(xiàn) RT-DETR 模型的推理部署,RT-DETR 與訓(xùn)練模型采用的是 COCO 數(shù)據(jù)集,最終我們可以獲取預(yù)測(cè)后的圖像結(jié)果,如圖所示:
上圖中展示了 RT-DETR 模型預(yù)測(cè)結(jié)果,同時(shí),我們對(duì)模型圖里過程中的關(guān)鍵信息以及推理結(jié)果進(jìn)行了打?。?/p>
[INFO] This is an RT-DETR model deployment case using C++! [INFO] Model path: E:\Model\RT-DETR\RTDETR_cropping\rtdetr_r50vd_6x_coco.onnx [INFO] Device name: CPU [INFO] Inference Model [INFO] Model name: Model from PaddlePaddle. [INFO] Input: [INFO] name: image [INFO] type: float [INFO] shape: [?,3,640,640] [INFO] Output: [INFO] name: stack_7.tmp_0_slice_0 [INFO] type: float [INFO] shape: [?,300,4] [INFO] name: stack_8.tmp_0_slice_0 [INFO] type: float [INFO] shape: [?,300,80] [INFO] Infer result: [INFO] class_id : 0, label : person, confidence : 0.928, left_top : [215, 327], right_bottom: [259, 468] [INFO] class_id : 0, label : person, confidence : 0.923, left_top : [260, 343], right_bottom: [309, 460] [INFO] class_id : 0, label : person, confidence : 0.893, left_top : [402, 346], right_bottom: [451, 478] [INFO] class_id : 0, label : person, confidence : 0.796, left_top : [456, 369], right_bottom: [507, 479] [INFO] class_id : 0, label : person, confidence : 0.830, left_top : [519, 360], right_bottom: [583, 479] [INFO] class_id : 33, label : kite, confidence : 0.836, left_top : [323, 159], right_bottom: [465, 213] [INFO] class_id : 33, label : kite, confidence : 0.805, left_top : [329, 64], right_bottom: [388, 85] [INFO] class_id : 33, label : kite, confidence : 0.822, left_top : [282, 217], right_bottom: [419, 267] [INFO] class_id : 0, label : person, confidence : 0.834, left_top : [294, 384], right_bottom: [354, 443] [INFO] class_id : 33, label : kite, confidence : 0.793, left_top : [504, 195], right_bottom: [522, 214] [INFO] class_id : 33, label : kite, confidence : 0.524, left_top : [233, 22], right_bottom: [242, 29] [INFO] class_id : 33, label : kite, confidence : 0.763, left_top : [116, 178], right_bottom: [136, 190] [INFO] class_id : 0, label : person, confidence : 0.601, left_top : [497, 380], right_bottom: [529, 479] [INFO] class_id : 33, label : kite, confidence : 0.764, left_top : [460, 251], right_bottom: [478, 268] [INFO] class_id : 33, label : kite, confidence : 0.605, left_top : [176, 236], right_bottom: [256, 257] [INFO] class_id : 0, label : person, confidence : 0.732, left_top : [154, 380], right_bottom: [210, 420] [INFO] class_id : 33, label : kite, confidence : 0.574, left_top : [221, 264], right_bottom: [342, 312] [INFO] class_id : 33, label : kite, confidence : 0.588, left_top : [97, 316], right_bottom: [155, 359] [INFO] class_id : 33, label : kite, confidence : 0.523, left_top : [171, 317], right_bottom: [227, 357] [INFO] class_id : 33, label : kite, confidence : 0.657, left_top : [363, 120], right_bottom: [375, 129] [INFO] class_id : 0, label : person, confidence : 0.698, left_top : [26, 341], right_bottom: [57, 425] [INFO] class_id : 33, label : kite, confidence : 0.798, left_top : [242, 124], right_bottom: [263, 135] [INFO] class_id : 33, label : kite, confidence : 0.528, left_top : [218, 178], right_bottom: [451, 241] [INFO] class_id : 33, label : kite, confidence : 0.685, left_top : [430, 29], right_bottom: [449, 43] [INFO] class_id : 33, label : kite, confidence : 0.640, left_top : [363, 120], right_bottom: [375, 129] [INFO] class_id : 33, label : kite, confidence : 0.559, left_top : [161, 193], right_bottom: [171, 199]
左滑查看更多
7. 總結(jié)
在本項(xiàng)目中,我們介紹了 OpenVINO C++ API部署自帶后處理的 RT-DETR 模型的案例,并結(jié)合該模型的處理方式封裝完整的代碼案例,實(shí)現(xiàn)了在英特爾平臺(tái)使用 OpenVINO加速深度學(xué)習(xí)模型,有助于大家以后落地 RT-DETR 模型在工業(yè)上的應(yīng)用。
在下一篇文章《基于OpenVINO Python C#部署RT-DETR模型》中,我們將基于 C# API 接口,實(shí)現(xiàn) RT-DETR模型的部署,并且基于開發(fā)的代碼,對(duì)比不同平臺(tái)的推理速度。如果大家有興趣,可以先關(guān)注本項(xiàng)目代碼倉(cāng)庫,獲取項(xiàng)目實(shí)現(xiàn)源碼。
審核編輯:湯梓紅
-
英特爾
+關(guān)注
關(guān)注
61文章
10247瀏覽量
178544 -
C++
+關(guān)注
關(guān)注
22文章
2120瀏覽量
76577 -
模型
+關(guān)注
關(guān)注
1文章
3618瀏覽量
51545 -
python
+關(guān)注
關(guān)注
56文章
4849瀏覽量
89227 -
OpenVINO
+關(guān)注
關(guān)注
0文章
116瀏覽量
689
原文標(biāo)題:基于 OpenVINO? C++ API 部署 RT-DETR 模型 | 開發(fā)者實(shí)戰(zhàn)
文章出處:【微信號(hào):英特爾物聯(lián)網(wǎng),微信公眾號(hào):英特爾物聯(lián)網(wǎng)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
C#集成OpenVINO?:簡(jiǎn)化AI模型部署
在Ubuntu上搭建OpenVINO C++程序開發(fā)環(huán)境
如何使用OpenVINO C++ API部署FastSAM模型
為什么無法在運(yùn)行時(shí)C++推理中讀取OpenVINO?模型?
在C++中使用OpenVINO工具包部署YOLOv5模型
簡(jiǎn)單聊聊目標(biāo)檢測(cè)新范式RT-DETR的骨干:HGNetv2
介紹RT-DETR兩種風(fēng)格的onnx格式和推理方式
用OpenVINO? C++ API編寫YOLOv8-Seg實(shí)例分割模型推理程序
基于OpenVINO Python API部署RT-DETR模型
如何使用OpenVINO Python API部署FastSAM模型
OpenVINO? Java API應(yīng)用RT-DETR做目標(biāo)檢測(cè)器實(shí)戰(zhàn)
用OpenVINO C# API在intel平臺(tái)部署YOLOv10目標(biāo)檢測(cè)模型
使用OpenVINO Model Server在哪吒開發(fā)板上部署模型

基于OpenVINO C++ API部署RT-DETR模型
評(píng)論