当前位置:   article > 正文

yolov7模型部署——代码实现(python版和c#版)_yolov7代码

yolov7代码

忙了两个星期,终于把c#版onnx调用整合到项目中,并和UI功能结合起来了~~~也终于腾出时间来总结一下,都快忘记踩过什么坑了T_T。

一,python版

        python版比较容易,毕竟有官方的detect.py指导,据说之前官方放了个使用onnx推理的ipynb文件,但很快就删了~~~参考这篇博客。通过模型结构图查看模型输入要求尺寸为[1 3 640 640]。

onnx调用的基本思想步骤就是:

  1. 根据EP建立session:
    1. providers = ['CUDAExecutionProvider', 'CPUExecutionProvider'] if cuda else ['CPUExecutionProvider']
    2. session = ort.InferenceSession(w, providers=providers)
  2. 调用session.run:
    outputs = session.run(outname, inp)[0]
  3. 解析并可视化结果

具体实现步骤为:

  • Resize原始图片,处理成onnx模型需要的大小,这个过程是通过letterbox()函数实现的。公主的模型输入尺寸为[640, 640]。根据官方提供的思想resize的时候需要保持原始图片的长宽比。因此需要首先计算与所需尺寸相差的最小ratio(即根据原始长度长,宽较长一边计算出的ratio),将图片resize之后,剩下的空隙用灰色来填充。公主输入图片原始尺寸为[512, 555, 3],转换之后为[640, 640, 3],如下图所示:

  • 将图像转为RGB格式,并增加一维以符合输入尺寸,通过以下命令实现
    image = image.transpose((2, 0, 1))
    image = np.expand_dims(image, 0)
    
  • 调用onnx模型
  • 可视化输出结果,输出是个二维n*7数组,n代表检测结果数,每一条结果的7个值分别代表:[batch_id, bbox左上角横坐标,bbox左上角纵坐标,bbox右下角横坐标,bbox右下角纵坐标,classIndex,置信度]。这里可视化bbox时注意,得出的bbox坐标为resize之后的图像中坐标,需要根据resize时用到的ratio和padding数来还原出原始坐标,才可以在原始图像中显示出来。

python代码:

  1. import cv2
  2. import time
  3. import requests
  4. import random
  5. import numpy as np
  6. import onnxruntime as ort
  7. import onnx
  8. import torch
  9. # import torchvision
  10. # from PIL import Image
  11. # from pathlib import Path
  12. # from collections import OrderedDict, namedtuple
  13. cuda = torch.cuda.is_available()
  14. w = "bestE2E.onnx" # 模型名称
  15. # 判断模型是否有效,这步可以省略
  16. onnx_model = onnx.load(w)
  17. try:
  18. onnx.checker.check_model(onnx_model)
  19. except Exception:
  20. print("incorrect model")
  21. exit()
  22. else:
  23. print("correct model")
  24. # 创建session
  25. providers = ['CUDAExecutionProvider', 'CPUExecutionProvider'] if cuda else ['CPUExecutionProvider']
  26. session = ort.InferenceSession(w, providers=providers)
  27. # Resize原始图像,预处理
  28. def letterbox(im, new_shape=(640, 640), color=(114, 114, 114), auto=True, scaleup=True, stride=32):
  29. # Resize and pad image while meeting stride-multiple constraints
  30. shape = im.shape[:2] # current shape [height, width]
  31. if isinstance(new_shape, int):
  32. new_shape = (new_shape, new_shape)
  33. # Scale ratio (new / old)
  34. r = min(new_shape[0] / shape[0], new_shape[1] / shape[1])
  35. if not scaleup: # only scale down, do not scale up (for better val mAP)
  36. r = min(r, 1.0)
  37. # Compute padding
  38. new_unpad = int(round(shape[1] * r)), int(round(shape[0] * r))
  39. dw, dh = new_shape[1] - new_unpad[0], new_shape[0] - new_unpad[1] # wh padding
  40. if auto: # minimum rectangle
  41. dw, dh = np.mod(dw, stride), np.mod(dh, stride) # wh padding
  42. dw /= 2 # divide padding into 2 sides
  43. dh /= 2
  44. print(shape[::-1])
  45. if shape[::-1] != new_unpad: # resize
  46. im = cv2.resize(im, new_unpad, interpolation=cv2.INTER_LINEAR)
  47. top, bottom = int(round(dh - 0.1)), int(round(dh + 0.1))
  48. left, right = int(round(dw - 0.1)), int(round(dw + 0.1))
  49. im = cv2.copyMakeBorder(im, top, bottom, left, right, cv2.BORDER_CONSTANT, value=color) # add border
  50. return im, r, (dw, dh)
  51. # 类别名称,根据需要自己修改
  52. names = ['bubbles']
  53. colors = {name: [random.randint(0, 255) for _ in range(3)] for i, name in enumerate(names)}
  54. # 读入待检测图像
  55. img_path = r"D:/YOLOV7/img/test.bmp"
  56. img = cv2.imread(img_path)
  57. # BGR to RGB,并增加一维
  58. image = img.copy()
  59. image, ratio, dwdh = letterbox(image, auto=False)
  60. image = image.transpose((2, 0, 1))
  61. image = np.expand_dims(image, 0)
  62. image = np.ascontiguousarray(image)
  63. # 归一化
  64. im = image.astype(np.float32)
  65. im /= 255
  66. outname = [i.name for i in session.get_outputs()]
  67. inname = [i.name for i in session.get_inputs()]
  68. # 调用模型得出结果,并记录时间
  69. inp = {inname[0]: im}
  70. t1 = time.time()
  71. outputs = session.run(outname, inp)[0]
  72. print('inference time :%.4f' % (time.time() - t1))
  73. ori_images = [img.copy()]
  74. # 可视化结果
  75. for i, (batch_id, x0, y0, x1, y1, cls_id, score) in enumerate(outputs):
  76. image = ori_images[int(batch_id)]
  77. box = np.array([x0, y0, x1, y1])
  78. box -= np.array(dwdh * 2)
  79. box /= ratio
  80. box = box.round().astype(np.int32).tolist()
  81. cls_id = int(cls_id)
  82. score = round(float(score), 3)
  83. name = names[cls_id]
  84. color = colors[name]
  85. name += ' ' + str(score)
  86. cv2.rectangle(image, box[:2], box[2:], color, 2)
  87. cv2.putText(image, name, (box[0], box[1] - 2), cv2.FONT_HERSHEY_SIMPLEX, 0.75, [225, 255, 255], thickness=2)
  88. cv2.imshow('dddd', ori_images[0])
  89. cv2.waitKey(0)
  90. cv2.destroyAllWindows()

结果显示:

二,C# 版

 公主做的demo界面如下:

 步骤与python版一模一样,公主一句一句翻译过来的。中间遇到过很多坑,但事情太多忘记一条一条记录了,有问题随时欢迎评论区讨论,完整代码如下:

  1. private void btn_analyze_Click(object sender, EventArgs e){
  2. // 原始图像切割为3份,分成1024*1024
  3. Mat img_img = this.img_mat;
  4. Mat[] subImg = new Mat[3];
  5. int w = Width / 3;
  6. subImg[0] = img_img[0, 1024, startX, startX + w];
  7. subImg[1] = img_img[0, 1024, startX + w, startX + w * 2];
  8. subImg[2] = img_img[0, 1024, startX + w * 2, startX + rect.Width - edge * 2];
  9. // pictureBox对象“们”存为数组
  10. PictureBox[] picbox = new PictureBox[3] { this.pictureBox2, this.pictureBox3, this.pictureBox4 };
  11. for(int i = 0; i < 3; i++)
  12. {
  13. int top = 0, left = 0;
  14. double ratio = 0.0;
  15. // after resize and padding
  16. Mat img_copy = new Mat();
  17. subImg[i].CopyTo(img_copy);
  18. // letterbox实现Resize图像
  19. Mat imageRgb = letterbox(img_copy, imgSize, out top, out left, out ratio);
  20. // 将Mat格式图像转化为onnx模型需要的tensor张量
  21. Bitmap imageBmp = BitmapConverter.ToBitmap(imageRgb);
  22. Tensor<float> input = FastToOnnxTensor(imageBmp);
  23. var inputs = new List<NamedOnnxValue>
  24. {
  25. NamedOnnxValue.CreateFromTensor("images", input)
  26. };
  27. // 调用onnx模型,保存结果
  28. IDisposableReadOnlyCollection<DisposableNamedOnnxValue> results = session.Run(inputs);
  29. // 图像后处理,并可视化
  30. var resultsArray = results.ToArray()[0];
  31. Tensor<float> tensors = resultsArray.AsTensor<float>();
  32. var array = tensors.ToArray();
  33. Mat newMat = new Mat();
  34. Cv2.CvtColor(subImg[i], newMat, ColorConversionCodes.GRAY2RGB);
  35. if (array.Length > 0)
  36. {
  37. for (int j = 0; j < tensors.Dimensions[0]; j++)
  38. {
  39. int startIndex = j * tensors.Dimensions[1];
  40. Scalar color = new Scalar(127, 255, 0); // 灰度颜色
  41. int lefttop_x = (int)((array[startIndex + 1] - left) / ratio);
  42. int lefttop_y = (int)((array[startIndex + 2] - top) / ratio);
  43. int rightbottom_x = (int)((array[startIndex + 3] - left) / ratio);
  44. int rightbottom_y = (int)((array[startIndex + 4] - top) / ratio);
  45. Cv2.Rectangle(newMat, new OpenCvSharp.Point(lefttop_x, lefttop_y),
  46. new OpenCvSharp.Point(rightbottom_x, rightbottom_y),
  47. color, 2);
  48. Cv2.PutText(newMat, LabelMap.Labels[(int)array[startIndex + 5]],
  49. new OpenCvSharp.Point(lefttop_x, lefttop_y - 5),
  50. HersheyFonts.HersheySimplex, 0.75, color, 2);
  51. }
  52. }
  53. picbox[i].Image = BitmapConverter.ToBitmap(newMat);
  54. }
  55. }
  56. // Load表格时创建session
  57. private void OnnxDemo_Load(object sender, EventArgs e){
  58. options = SessionOptions.MakeSessionOptionWithCudaProvider(0);
  59. session = new InferenceSession(path, options);
  60. }
  61. // letterbox预处理图像
  62. private Mat letterbox(Mat subImg, int newSize, out int top, out int left, out double ratio)
  63. {
  64. int width = subImg.Cols;
  65. int height = subImg.Rows;
  66. ratio = Math.Min(((double)newSize / (double)width), ((double)newSize / (double)height));
  67. int[] new_unpad = { (int)(Math.Round(height * ratio)), (int)(Math.Round(width * ratio)) };
  68. int[] dwh = { (newSize - new_unpad[0]) / 2, (newSize - new_unpad[1]) / 2 };
  69. if (dwh != new_unpad)
  70. {
  71. Cv2.Resize(subImg, subImg, new OpenCvSharp.Size(new_unpad[1], new_unpad[0]));
  72. }
  73. top = (int)(Math.Round(dwh[0] - 0.1));
  74. int bottom = newSize - top - new_unpad[0];
  75. left = (int)(Math.Round(dwh[1] - 0.1));
  76. int right = newSize - left - new_unpad[1];
  77. Cv2.CopyMakeBorder(subImg, subImg, top, bottom, left, right, BorderTypes.Constant, Scalar.Gray);
  78. Cv2.CvtColor(subImg, subImg, ColorConversionCodes.GRAY2RGB); //Gray to RGB
  79. return subImg;
  80. }
  81. // Bitmap格式转化为Tensor张量
  82. public Tensor<float> FastToOnnxTensor(Bitmap source)
  83. {
  84. var floatArray = new float[source.Width * source.Height * 3];
  85. var bitmap_data = source.LockBits(new Rectangle(0, 0, source.Width, source.Height), ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
  86. var bitmap_bytes = new byte[Math.Abs(bitmap_data.Stride) * source.Height];
  87. Marshal.Copy(bitmap_data.Scan0, bitmap_bytes, 0, bitmap_bytes.Length);
  88. int total_pixels_count = source.Width * source.Height;
  89. Parallel.For(0, total_pixels_count, (p_idx, state) =>
  90. {
  91. var g_idx = p_idx + total_pixels_count;
  92. var b_idx = p_idx + total_pixels_count * 2;
  93. floatArray[p_idx] = bitmap_bytes[p_idx * 3] / 255f;//R
  94. floatArray[g_idx] = bitmap_bytes[p_idx * 3 + 1] / 255f;//G
  95. floatArray[b_idx] = bitmap_bytes[p_idx * 3 + 2] / 255f;//B
  96. });
  97. source.UnlockBits(bitmap_data);
  98. return new DenseTensor<float>(new Memory<float>(floatArray), new int[] { 1, 3, source.Height, source.Width });
  99. }

静态变量:

  1. // 静态变量,用来保存类别名称
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Text;
  6. using System.Threading.Tasks;
  7. namespace OnnxDemo
  8. {
  9. public static class LabelMap
  10. {
  11. static LabelMap()
  12. {
  13. Labels = new string[]
  14. {
  15. "bubble"
  16. };
  17. }
  18. public static string[] Labels { set; get; }
  19. }
  20. }

运行结果:

 Nice~Nice~Nice~~~公主对结果还是灰常满意哒!!!

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

闽ICP备14008679号