当前位置:   article > 正文

昇腾NPU推理YOLOV10目标检测(C++)_yolov10 晟腾

yolov10 晟腾

1.准备工作

基础环境:

需要安装NPU固件驱动,CANN的包在昇腾官网下载,安装最新版就可以了。

C++环境搭建链接:

cplusplus/environment/catenation_environmental_guidance_CN.md · Ascend/samples - Gitee.com

按照上面的链接,需要安装:presentagent, opencv, ffmpeg+acllite

其中ffmpeg和opencv建议手动编译,主要注意下面框中的FFMPEG是YES就行,否则有BUG。

我的opencv命令是这样的,贴出来仅供参考:
 

cmake -D CMAKE_BUILD_TYPE=Release -D CMAKE_INSTALL_PREFIX=/usr/local -D OPENCV_EXTRA_MODULES_PATH=../../opencv_contrib-4.4.0/modules -D WITH_FFMPEG=ON -D FFMPEG_INCLUDE_DIR=/usr/local/include -D FFMPEG_LIB_DIR=/usr/local/lib .. 

yoloV10推理:

模型转换

昇腾推理的模型是OM,需要pt->onnx,转om,再推理。

在yoloV10官网下载预训练好的pt模型,官网链接:
https://github.com/THU-MIG/yolov10

补充:这里可以直接下载预训练好的onn模型:

https://github.com/THU-MIG/yolov10/releases

onnx转om:

用atc工具转换步骤:

1.生效环境,2.atc命令转换,得到om。这里贴出命令。

  1. source /usr/local/Ascend/ascend-toolkit/set_env.sh
  2. atc --model=yolov10m.onnx --framework=5 --output=yolov10m-detect --input_shape="images:1,3,640,640" --soc_version=Ascend910B

模型推理

前面的准备工作完了,下面分析一下yolov10模型的输入和输出,

输入处理

模型输入1,3,640,640-->和V8一样,rbg,chw,归一化,(需要注意的是,yolov10、yolov8预处理都没有标准化,yolov5有,之气被这个坑拖了好久,印象很深刻。)

可以看看这个链接,讲的真的很好:

YOLOv10推理详解及部署实现_yolov10部署-CSDN博客

话不多说,上代码:

  1. if (frame.empty()) {
  2. ERROR_LOG("The input of v10detect model is empty!");
  3. return FAILED;
  4. }
  5. //原图的宽和高
  6. this->src_Width_ = frame.cols;
  7. this->src_Height_ = frame.rows;
  8. //================前处理==================
  9. cv::resize(frame, frame, cv::Size(v10detect_modelHeight_,v10detect_modelWidth_)); // RESIZE
  10. cv::cvtColor(frame, frame, cv::COLOR_BGR2RGB); // BGR2RGB
  11. cv::Mat nchwMat(frame.rows, frame.cols * frame.channels(), CV_32FC1);
  12. uint8_t* ptMat = frame.ptr<uint8_t>(0);
  13. int height = frame.rows;
  14. int width = frame.cols;
  15. int channels = frame.channels();
  16. int area = height * width;
  17. for (int c = 0; c < channels; ++c)
  18. {
  19. for (int h = 0; h < height; ++h)
  20. {
  21. for (int w = 0; w < width; ++w)
  22. {
  23. int srcIdx = h * width * channels + w * channels + c;
  24. int dstIdx = c * area + h * width + w;
  25. float pixelValue = ptMat[srcIdx]/ 255.0f;
  26. nchwMat.at<float>(dstIdx) = pixelValue;
  27. }
  28. }
  29. }

推理

昇腾NPU的OM推理步骤大致是:

1.ACL资源初始化,只需要初始化一次

2.运行管理资源申请:

2.1指定Device(1、多),

2.2创建Context

2.3创建Stream。

3.模型加载:

3.1从系统文件加载模型

3.2获取模型描述信息,获取模型输入输出个数,查询模型输入输出字节大小aclmdlCreateDesc()aclmdlGetDesc(Desc_,Id_)  等API。

3.3根据输入输出的大小和数量aclmdlGetInputSizeByIndex(Decs_,Id_)等API,分别创建模型Device侧的输入输出数据集aclmdlCreateDataset()、创建数据缓冲区aclCreateDataBuffer(viod* inputbuffer_, size_t size_),有几个就创建几个、按照Index顺序添加数据到缓冲区aclmdlAddDatasetBuffer(Dataset_,buffer_)

4.执行推理:

4.1输入从Host侧拷贝到Device侧,

4.2推理:同步API:aclmdlExecute(Id,inputDataSet_,ootputDataset)

4.3输出从Device侧拷贝到Host侧。

5.资源释放:

5.1释放输入输出数据缓冲区,

5.2卸载模型,

5.3销毁stream

5.4销毁context

6.ACL资源初始化,只需要去初始化一次

输出处理

模型输出1,300,6-->6表示[left,top,right,bottom,confidence,classification](这里改动很好,再也不需要x,y,w,h转left,top,right,bottom了,不需要转置了,不需要NMS了,只需要仿射变换和删除阈值过低的对象就行,这里比以前版本的yolo的后处理省事太多了,感动。)。

从NPU芯片推理得到输出后,从NPU的device侧搬运到我们的Host侧,得到输出,在进行后处理,代码如下:
 

  1. //计算仿射矩阵和仿射逆矩阵
  2. void Objectv10Detect::Affine(uint32_t src_Width, uint32_t src_Height, uint32_t Dst_Width, uint32_t Dst_Height,
  3. std::vector<std::vector<float>>& Affine_Matrix, std::vector<std::vector<float>>& Inverse_Affine_Matrix) {
  4. float scalw = static_cast<float>(Dst_Width) / src_Width;
  5. float scalh = static_cast<float>(Dst_Height) / src_Height;
  6. // 仿射变换矩阵
  7. Affine_Matrix = {{scalw, 0, 0},{0, scalh, 0}};
  8. // 仿射逆矩阵
  9. Inverse_Affine_Matrix = {{1 / scalw, 0, 0},{0, 1 / scalh, 0}};
  10. }
  11. Result Objectv10Detect::Postprocess(aclmdlDataset* modelOutput,std::vector<std::vector<float>>& results)
  12. {
  13. // Get feature vector data
  14. uint32_t dataSize = 0;
  15. float* featureData = (float*)GetInferenceOutputItem(dataSize, modelOutput,
  16. 0);
  17. if (featureData == nullptr) return FAILED;
  18. //输出是3006
  19. results.resize(300);
  20. for (size_t i = 0; i < 300; ++i) {
  21. results[i].assign(featureData + i * 6, featureData + (i + 1) * 6);
  22. }
  23. if (v10detect_runMode_ == ACL_HOST) {
  24. delete[] ((float*)featureData);
  25. }
  26. //保留置信度大于阈值的
  27. std::vector<std::vector<float>> updated_results;
  28. for (const std::vector<float>& row : results) {
  29. if ( row[4] >= this->conf_th_) {
  30. updated_results.push_back(row);
  31. }
  32. }
  33. //更新results
  34. results=updated_results;
  35. //解码关键点
  36. std::vector<std::vector<float>> M, IM; // 声明仿射矩阵 M、仿射逆矩阵 IM
  37. Objectv10Detect::Affine(this->src_Width_, this->src_Height_, this->v10detect_modelWidth_, this->v10detect_modelHeight_, M, IM); // 计算仿射矩阵
  38. for (int i=0;i<results.size();++i)
  39. {
  40. // 提取信息:需要注意的是,v10输出不再是cx,cy,w,h而是修改为了left,top,right,bottom
  41. float left = results[i][0];//left
  42. float top = results[i][1];//top
  43. float right = results[i][2];//right
  44. float bottom = results[i][3];//bottom
  45. //仿射变换解码
  46. left = IM[0][0] * left + IM[0][2];
  47. top = IM[1][1] * top + IM[1][2];
  48. right = IM[0][0] * right + IM[0][2];
  49. bottom = IM[1][1] * bottom + IM[1][2];
  50. results[i][0]=left;
  51. results[i][1]=top;
  52. results[i][2]=right;
  53. results[i][3]=bottom;
  54. }
  55. return SUCCESS;
  56. }

网上随便下载一个图片,测试,结果如下,终于大功告成了:

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/942432
推荐阅读
相关标签
  

闽ICP备14008679号