当前位置:   article > 正文

Opencv C++实现yolov5部署onnx模型完成目标检测_c++ opencv yolo onnx

c++ opencv yolo onnx

代码分析:

头文件
  1. #include <fstream> //文件
  2. #include <sstream> //流
  3. #include <iostream>
  4. #include <opencv2/dnn.hpp> //深度学习模块-仅提供推理功能
  5. #include <opencv2/imgproc.hpp> //图像处理模块
  6. #include <opencv2/highgui.hpp> //媒体的输入输出/视频捕捉/图像和视频的编码解码/图形界面的接口
命名空间
  1. using namespace cv;
  2. using namespace dnn;
  3. using namespace std;
结构体 Net_config
  1. struct Net_config{
  2. float confThreshold; // 置信度阈值
  3. float nmsThreshold; // 非最大抑制阈值
  4. float objThreshold; // 对象置信度阈值
  5. string modelpath;
  6. };

里面存了三个阈值和模型地址,其中置信度,顾名思义,看检测出来的物体的精准度。以测量值为中心,在一定范围内,真值出现在该范围内的几率。

endsWith()函数 判断sub是不是s的子串
  1. int endsWith(string s, string sub) {
  2. return s.rfind(sub) == (s.length() - sub.length()) ? 1 : 0;
  3. }
anchors_640图像接收数组
  1. const float anchors_640[3][6] = { {10.0, 13.0, 16.0, 30.0, 33.0, 23.0},
  2. {30.0, 61.0, 62.0, 45.0, 59.0, 119.0},
  3. {116.0, 90.0, 156.0, 198.0, 373.0, 326.0} };

根据图像大小,选择相应长度的二维数组。深度为3,每层6个位置。

YOLO类
  1. class YOLO{
  2. public:
  3. YOLO(Net_config config); //构造函数
  4. void detect(Mat& frame); //通过图像参数,进行目标检测
  5. private:
  6. float* anchors;
  7. int num_stride;
  8. int inpWidth;
  9. int inpHeight;
  10. vector<string> class_names;
  11. int num_class;
  12. float confThreshold;
  13. float nmsThreshold;
  14. float objThreshold;
  15. const bool keep_ratio = true;
  16. Net net;
  17. void drawPred(float conf, int left, int top, int right, int bottom, Mat& frame, int classid);
  18. Mat resize_image(Mat srcimg, int* newh, int* neww, int* top, int* left);
  19. };
YOLO类构造函数的重载
  1. YOLO::YOLO(Net_config config){
  2. this->confThreshold = config.confThreshold;
  3. this->nmsThreshold = config.nmsThreshold;
  4. this->objThreshold = config.objThreshold;
  5. this->net = readNet(config.modelpath);
  6. ifstream ifs("class.names"); //class.name中写入标签内容,当前只有'person',位置与当前.cpp文件同级
  7. string line;
  8. while (getline(ifs, line)) this->class_names.push_back(line);
  9. this->num_class = class_names.size();
  10. if (endsWith(config.modelpath, "6.onnx")){ //根据onnx的输入图像格式 选择分辨率 当前为1280x1280 可灵活调整
  11. anchors = (float*)anchors_1280;
  12. this->num_stride = 4; //深度
  13. this->inpHeight = 1280; //高
  14. this->inpWidth = 1280; //宽
  15. }
  16. else{ //当前为640x640 可以resize满足onnx需求 也可以调整数组或if中的onnx判断
  17. anchors = (float*)anchors_640;
  18. this->num_stride = 3;
  19. this->inpHeight = 640;
  20. this->inpWidth = 640;
  21. }
  22. }

重新规定图像大小函数 resize_image()
  1. Mat YOLO::resize_image(Mat srcimg, int* newh, int* neww, int* top, int* left){//传入图像以及需要改变的参数
  2. int srch = srcimg.rows, srcw = srcimg.cols;
  3. *newh = this->inpHeight;
  4. *neww = this->inpWidth;
  5. Mat dstimg;
  6. if (this->keep_ratio && srch != srcw) {
  7. float hw_scale = (float)srch / srcw;
  8. if (hw_scale > 1) {
  9. *newh = this->inpHeight;
  10. *neww = int(this->inpWidth / hw_scale);
  11. resize(srcimg, dstimg, Size(*neww, *newh), INTER_AREA);
  12. *left = int((this->inpWidth - *neww) * 0.5);
  13. copyMakeBorder(dstimg, dstimg, 0, 0, *left, this->inpWidth - *neww - *left, BORDER_CONSTANT, 114);
  14. }else {
  15. *newh = (int)this->inpHeight * hw_scale;
  16. *neww = this->inpWidth;
  17. resize(srcimg, dstimg, Size(*neww, *newh), INTER_AREA);
  18. *top = (int)(this->inpHeight - *newh) * 0.5;
  19. copyMakeBorder(dstimg, dstimg, *top, this->inpHeight - *newh - *top, 0, 0, BORDER_CONSTANT, 114);
  20. }
  21. }else {
  22. resize(srcimg, dstimg, Size(*neww, *newh), INTER_AREA);
  23. }
  24. return dstimg;
  25. }

绘制预测的边界框函数 drawPred()
  1. void YOLO::drawPred(float conf, int left, int top, int right, int bottom, Mat& frame, int classid){
  2. //绘制一个显示边界框的矩形
  3. rectangle(frame, Point(left, top), Point(right, bottom), Scalar(0, 0, 255), 2);
  4. //获取类名的标签及其置信度
  5. string label = format("%.2f", conf);
  6. label = this->class_names[classid] + ":" + label;
  7. //在边界框顶部显示标签
  8. int baseLine;
  9. Size labelSize = getTextSize(label, FONT_HERSHEY_SIMPLEX, 0.5, 1, &baseLine);
  10. top = max(top, labelSize.height);
  11. //rectangle(frame, Point(left, top - int(1.5 * labelSize.height)), Point(left + int(1.5 * labelSize.width), top + baseLine), Scalar(0, 255, 0), FILLED);
  12. putText(frame, label, Point(left, top), FONT_HERSHEY_SIMPLEX, 0.75, Scalar(0, 255, 0), 1);
  13. }

【核心代码】检测函数 detect()
  1. void YOLO::detect(Mat& frame){
  2. int newh = 0, neww = 0, padh = 0, padw = 0;
  3. Mat dstimg = this->resize_image(frame, &newh, &neww, &padh, &padw);
  4. Mat blob = blobFromImage(dstimg, 1 / 255.0, Size(this->inpWidth, this->inpHeight), Scalar(0, 0, 0), true, false);
  5. this->net.setInput(blob);
  6. vector<Mat> outs;
  7. this->net.forward(outs, this->net.getUnconnectedOutLayersNames());
  8. int num_proposal = outs[0].size[1];
  9. int nout = outs[0].size[2];
  10. if (outs[0].dims > 2){
  11. outs[0] = outs[0].reshape(0, num_proposal);
  12. }
  13. vector<float> confidences;
  14. vector<Rect> boxes;
  15. vector<int> classIds;
  16. float ratioh = (float)frame.rows / newh, ratiow = (float)frame.cols / neww;
  17. int n = 0, q = 0, i = 0, j = 0, row_ind = 0; //xmin,ymin,xamx,ymax,box_score,class_score
  18. float* pdata = (float*)outs[0].data;
  19. for (n = 0; n < this->num_stride; n++){ //特征图尺度
  20. const float stride = pow(2, n + 3);
  21. int num_grid_x = (int)ceil((this->inpWidth / stride));
  22. int num_grid_y = (int)ceil((this->inpHeight / stride));
  23. for (q = 0; q < 3; q++){
  24. const float anchor_w = this->anchors[n * 6 + q * 2];
  25. const float anchor_h = this->anchors[n * 6 + q * 2 + 1];
  26. for (i = 0; i < num_grid_y; i++){
  27. for (j = 0; j < num_grid_x; j++){
  28. float box_score = pdata[4];
  29. if (box_score > this->objThreshold){
  30. Mat scores = outs[0].row(row_ind).colRange(5, nout);
  31. Point classIdPoint;
  32. double max_class_socre;
  33. //获取最高分的值和位置
  34. minMaxLoc(scores, 0, &max_class_socre, 0, &classIdPoint);
  35. max_class_socre *= box_score;
  36. if (max_class_socre > this->confThreshold){
  37. const int class_idx = classIdPoint.x;
  38. float cx = pdata[0]; //cx
  39. float cy = pdata[1]; //cy
  40. float w = pdata[2]; //w
  41. float h = pdata[3]; //h
  42. int left = int((cx - padw - 0.5 * w) * ratiow);
  43. int top = int((cy - padh - 0.5 * h) * ratioh);
  44. confidences.push_back((float)max_class_socre);
  45. boxes.push_back(Rect(left, top, (int)(w * ratiow), (int)(h * ratioh)));
  46. classIds.push_back(class_idx);
  47. }
  48. }
  49. row_ind++;
  50. pdata += nout;
  51. }
  52. }
  53. }
  54. }
  55. // 执行非最大抑制以消除冗余重叠框
  56. // 置信度较低
  57. vector<int> indices;
  58. dnn::NMSBoxes(boxes, confidences, this->confThreshold, this->nmsThreshold, indices);
  59. for (size_t i = 0; i < indices.size(); ++i){
  60. int idx = indices[i];
  61. Rect box = boxes[idx];
  62. this->drawPred(confidences[idx], box.x, box.y,box.x + box.width, box.y + box.height, frame, classIds[idx]);
  63. }
  64. }

主函数

  1. int main(){
  2. //加载onnx模型
  3. Net_config yolo_nets = { 0.3, 0.5, 0.3, "yolov5n_person_320.onnx" };
  4. YOLO yolo_model(yolo_nets);
  5. //加载单张图片
  6. string imgpath = "112.png";
  7. Mat srcimg = imread(imgpath);
  8. //开始检测
  9. yolo_model.detect(srcimg);
  10. static const string kWinName = "Deep learning object detection in OpenCV";
  11. namedWindow(kWinName, WINDOW_NORMAL);
  12. imshow(kWinName, srcimg); //显示图片
  13. waitKey(0); //保持停留
  14. destroyAllWindows(); //关闭窗口并取消分配任何相关的内存使用
  15. }

完整代码

  1. #include <fstream>
  2. #include <sstream>
  3. #include <iostream>
  4. #include <opencv2/dnn.hpp>
  5. #include <opencv2/imgproc.hpp>
  6. #include <opencv2/highgui.hpp>
  7. using namespace cv;
  8. using namespace dnn;
  9. using namespace std;
  10. struct Net_config
  11. {
  12. float confThreshold; // Confidence threshold
  13. float nmsThreshold; // Non-maximum suppression threshold
  14. float objThreshold; //Object Confidence threshold
  15. string modelpath;
  16. };
  17. int endsWith(string s, string sub) {
  18. return s.rfind(sub) == (s.length() - sub.length()) ? 1 : 0;
  19. }
  20. const float anchors_640[3][6] = { {10.0, 13.0, 16.0, 30.0, 33.0, 23.0},
  21. {30.0, 61.0, 62.0, 45.0, 59.0, 119.0},
  22. {116.0, 90.0, 156.0, 198.0, 373.0, 326.0} };
  23. const float anchors_1280[4][6] = { {19, 27, 44, 40, 38, 94},
  24. {96, 68, 86, 152, 180, 137},
  25. {140, 301, 303, 264, 238, 542},
  26. {436, 615, 739, 380, 925, 792} };
  27. class YOLO
  28. {
  29. public:
  30. YOLO(Net_config config);
  31. void detect(Mat& frame);
  32. private:
  33. float* anchors;
  34. int num_stride;
  35. int inpWidth;
  36. int inpHeight;
  37. vector<string> class_names;
  38. int num_class;
  39. float confThreshold;
  40. float nmsThreshold;
  41. float objThreshold;
  42. const bool keep_ratio = true;
  43. Net net;
  44. void drawPred(float conf, int left, int top, int right, int bottom, Mat& frame, int classid);
  45. Mat resize_image(Mat srcimg, int* newh, int* neww, int* top, int* left);
  46. };
  47. YOLO::YOLO(Net_config config)
  48. {
  49. this->confThreshold = config.confThreshold;
  50. this->nmsThreshold = config.nmsThreshold;
  51. this->objThreshold = config.objThreshold;
  52. this->net = readNet(config.modelpath);
  53. ifstream ifs("class.names");
  54. string line;
  55. while (getline(ifs, line)) this->class_names.push_back(line);
  56. this->num_class = class_names.size();
  57. if (endsWith(config.modelpath, "6.onnx"))
  58. {
  59. anchors = (float*)anchors_1280;
  60. this->num_stride = 4;
  61. this->inpHeight = 1280;
  62. this->inpWidth = 1280;
  63. }
  64. else
  65. {
  66. anchors = (float*)anchors_640;
  67. this->num_stride = 3;
  68. this->inpHeight = 640;
  69. this->inpWidth = 640;
  70. }
  71. }
  72. Mat YOLO::resize_image(Mat srcimg, int* newh, int* neww, int* top, int* left)
  73. {
  74. int srch = srcimg.rows, srcw = srcimg.cols;
  75. *newh = this->inpHeight;
  76. *neww = this->inpWidth;
  77. Mat dstimg;
  78. if (this->keep_ratio && srch != srcw) {
  79. float hw_scale = (float)srch / srcw;
  80. if (hw_scale > 1) {
  81. *newh = this->inpHeight;
  82. *neww = int(this->inpWidth / hw_scale);
  83. resize(srcimg, dstimg, Size(*neww, *newh), INTER_AREA);
  84. *left = int((this->inpWidth - *neww) * 0.5);
  85. copyMakeBorder(dstimg, dstimg, 0, 0, *left, this->inpWidth - *neww - *left, BORDER_CONSTANT, 114);
  86. }
  87. else {
  88. *newh = (int)this->inpHeight * hw_scale;
  89. *neww = this->inpWidth;
  90. resize(srcimg, dstimg, Size(*neww, *newh), INTER_AREA);
  91. *top = (int)(this->inpHeight - *newh) * 0.5;
  92. copyMakeBorder(dstimg, dstimg, *top, this->inpHeight - *newh - *top, 0, 0, BORDER_CONSTANT, 114);
  93. }
  94. }
  95. else {
  96. resize(srcimg, dstimg, Size(*neww, *newh), INTER_AREA);
  97. }
  98. return dstimg;
  99. }
  100. void YOLO::drawPred(float conf, int left, int top, int right, int bottom, Mat& frame, int classid) // Draw the predicted bounding box
  101. {
  102. //Draw a rectangle displaying the bounding box
  103. rectangle(frame, Point(left, top), Point(right, bottom), Scalar(0, 0, 255), 2);
  104. //Get the label for the class name and its confidence
  105. string label = format("%.2f", conf);
  106. label = this->class_names[classid] + ":" + label;
  107. //Display the label at the top of the bounding box
  108. int baseLine;
  109. Size labelSize = getTextSize(label, FONT_HERSHEY_SIMPLEX, 0.5, 1, &baseLine);
  110. top = max(top, labelSize.height);
  111. //rectangle(frame, Point(left, top - int(1.5 * labelSize.height)), Point(left + int(1.5 * labelSize.width), top + baseLine), Scalar(0, 255, 0), FILLED);
  112. putText(frame, label, Point(left, top), FONT_HERSHEY_SIMPLEX, 0.75, Scalar(0, 255, 0), 1);
  113. }
  114. void YOLO::detect(Mat& frame)
  115. {
  116. int newh = 0, neww = 0, padh = 0, padw = 0;
  117. Mat dstimg = this->resize_image(frame, &newh, &neww, &padh, &padw);
  118. Mat blob = blobFromImage(dstimg, 1 / 255.0, Size(this->inpWidth, this->inpHeight), Scalar(0, 0, 0), true, false);
  119. this->net.setInput(blob);
  120. vector<Mat> outs;
  121. this->net.forward(outs, this->net.getUnconnectedOutLayersNames());
  122. int num_proposal = outs[0].size[1];
  123. int nout = outs[0].size[2];
  124. if (outs[0].dims > 2)
  125. {
  126. outs[0] = outs[0].reshape(0, num_proposal);
  127. }
  128. /generate proposals
  129. vector<float> confidences;
  130. vector<Rect> boxes;
  131. vector<int> classIds;
  132. float ratioh = (float)frame.rows / newh, ratiow = (float)frame.cols / neww;
  133. int n = 0, q = 0, i = 0, j = 0, row_ind = 0; ///xmin,ymin,xamx,ymax,box_score,class_score
  134. float* pdata = (float*)outs[0].data;
  135. for (n = 0; n < this->num_stride; n++) ///特征图尺度
  136. {
  137. const float stride = pow(2, n + 3);
  138. int num_grid_x = (int)ceil((this->inpWidth / stride));
  139. int num_grid_y = (int)ceil((this->inpHeight / stride));
  140. for (q = 0; q < 3; q++) ///anchor
  141. {
  142. const float anchor_w = this->anchors[n * 6 + q * 2];
  143. const float anchor_h = this->anchors[n * 6 + q * 2 + 1];
  144. for (i = 0; i < num_grid_y; i++)
  145. {
  146. for (j = 0; j < num_grid_x; j++)
  147. {
  148. float box_score = pdata[4];
  149. if (box_score > this->objThreshold)
  150. {
  151. Mat scores = outs[0].row(row_ind).colRange(5, nout);
  152. Point classIdPoint;
  153. double max_class_socre;
  154. // Get the value and location of the maximum score
  155. minMaxLoc(scores, 0, &max_class_socre, 0, &classIdPoint);
  156. max_class_socre *= box_score;
  157. if (max_class_socre > this->confThreshold)
  158. {
  159. const int class_idx = classIdPoint.x;
  160. //float cx = (pdata[0] * 2.f - 0.5f + j) * stride; ///cx
  161. //float cy = (pdata[1] * 2.f - 0.5f + i) * stride; ///cy
  162. //float w = powf(pdata[2] * 2.f, 2.f) * anchor_w; ///w
  163. //float h = powf(pdata[3] * 2.f, 2.f) * anchor_h; ///h
  164. float cx = pdata[0]; ///cx
  165. float cy = pdata[1]; ///cy
  166. float w = pdata[2]; ///w
  167. float h = pdata[3]; ///h
  168. int left = int((cx - padw - 0.5 * w) * ratiow);
  169. int top = int((cy - padh - 0.5 * h) * ratioh);
  170. confidences.push_back((float)max_class_socre);
  171. boxes.push_back(Rect(left, top, (int)(w * ratiow), (int)(h * ratioh)));
  172. classIds.push_back(class_idx);
  173. }
  174. }
  175. row_ind++;
  176. pdata += nout;
  177. }
  178. }
  179. }
  180. }
  181. // Perform non maximum suppression to eliminate redundant overlapping boxes with
  182. // lower confidences
  183. vector<int> indices;
  184. dnn::NMSBoxes(boxes, confidences, this->confThreshold, this->nmsThreshold, indices);
  185. for (size_t i = 0; i < indices.size(); ++i)
  186. {
  187. int idx = indices[i];
  188. Rect box = boxes[idx];
  189. this->drawPred(confidences[idx], box.x, box.y,
  190. box.x + box.width, box.y + box.height, frame, classIds[idx]);
  191. }
  192. }
  193. int main()
  194. {
  195. Net_config yolo_nets = { 0.3, 0.5, 0.3, "yolov5n_person_320.onnx" };
  196. YOLO yolo_model(yolo_nets);
  197. //string imgpath = "112.png";
  198. //Mat srcimg = imread(imgpath);
  199. //yolo_model.detect(srcimg);
  200. int n = 588;
  201. for (int i = 1; i <= n; i++) {
  202. string s = to_string(i) + ".png";
  203. string imgpath = "F://test//p1//yanfa2//bh//cc//" + s;
  204. cout << imgpath << endl;
  205. Mat srcimg = imread(imgpath);
  206. yolo_model.detect(srcimg);
  207. imwrite("F://test//p2//yanfa2//bh//cc//" + s, srcimg);
  208. }
  209. //static const string kWinName = "Deep learning object detection in OpenCV";
  210. //namedWindow(kWinName, WINDOW_NORMAL);
  211. //imshow(kWinName, srcimg);
  212. //waitKey(0);
  213. //destroyAllWindows();
  214. }

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

闽ICP备14008679号