赞
踩
忙了两个星期,终于把c#版onnx调用整合到项目中,并和UI功能结合起来了~~~也终于腾出时间来总结一下,都快忘记踩过什么坑了T_T。
python版比较容易,毕竟有官方的detect.py指导,据说之前官方放了个使用onnx推理的ipynb文件,但很快就删了~~~参考这篇博客。通过模型结构图查看模型输入要求尺寸为[1 3 640 640]。
onnx调用的基本思想步骤就是:
- providers = ['CUDAExecutionProvider', 'CPUExecutionProvider'] if cuda else ['CPUExecutionProvider']
- session = ort.InferenceSession(w, providers=providers)
outputs = session.run(outname, inp)[0]
具体实现步骤为:
image = image.transpose((2, 0, 1)) image = np.expand_dims(image, 0)
python代码:
- import cv2
- import time
- import requests
- import random
- import numpy as np
- import onnxruntime as ort
- import onnx
- import torch
-
- # import torchvision
- # from PIL import Image
- # from pathlib import Path
- # from collections import OrderedDict, namedtuple
-
- cuda = torch.cuda.is_available()
- w = "bestE2E.onnx" # 模型名称
-
- # 判断模型是否有效,这步可以省略
- onnx_model = onnx.load(w)
- try:
- onnx.checker.check_model(onnx_model)
- except Exception:
- print("incorrect model")
- exit()
- else:
- print("correct model")
-
- # 创建session
- providers = ['CUDAExecutionProvider', 'CPUExecutionProvider'] if cuda else ['CPUExecutionProvider']
- session = ort.InferenceSession(w, providers=providers)
-
-
- # Resize原始图像,预处理
- def letterbox(im, new_shape=(640, 640), color=(114, 114, 114), auto=True, scaleup=True, stride=32):
- # Resize and pad image while meeting stride-multiple constraints
- shape = im.shape[:2] # current shape [height, width]
- if isinstance(new_shape, int):
- new_shape = (new_shape, new_shape)
-
- # Scale ratio (new / old)
- r = min(new_shape[0] / shape[0], new_shape[1] / shape[1])
- if not scaleup: # only scale down, do not scale up (for better val mAP)
- r = min(r, 1.0)
-
- # Compute padding
- new_unpad = int(round(shape[1] * r)), int(round(shape[0] * r))
- dw, dh = new_shape[1] - new_unpad[0], new_shape[0] - new_unpad[1] # wh padding
-
- if auto: # minimum rectangle
- dw, dh = np.mod(dw, stride), np.mod(dh, stride) # wh padding
-
- dw /= 2 # divide padding into 2 sides
- dh /= 2
- print(shape[::-1])
- if shape[::-1] != new_unpad: # resize
- im = cv2.resize(im, new_unpad, interpolation=cv2.INTER_LINEAR)
- top, bottom = int(round(dh - 0.1)), int(round(dh + 0.1))
- left, right = int(round(dw - 0.1)), int(round(dw + 0.1))
- im = cv2.copyMakeBorder(im, top, bottom, left, right, cv2.BORDER_CONSTANT, value=color) # add border
- return im, r, (dw, dh)
-
-
- # 类别名称,根据需要自己修改
- names = ['bubbles']
- colors = {name: [random.randint(0, 255) for _ in range(3)] for i, name in enumerate(names)}
-
-
- # 读入待检测图像
- img_path = r"D:/YOLOV7/img/test.bmp"
- img = cv2.imread(img_path)
-
- # BGR to RGB,并增加一维
- image = img.copy()
- image, ratio, dwdh = letterbox(image, auto=False)
- image = image.transpose((2, 0, 1))
- image = np.expand_dims(image, 0)
- image = np.ascontiguousarray(image)
-
- # 归一化
- im = image.astype(np.float32)
- im /= 255
-
- outname = [i.name for i in session.get_outputs()]
- inname = [i.name for i in session.get_inputs()]
-
- # 调用模型得出结果,并记录时间
- inp = {inname[0]: im}
- t1 = time.time()
- outputs = session.run(outname, inp)[0]
- print('inference time :%.4f' % (time.time() - t1))
-
- ori_images = [img.copy()]
-
- # 可视化结果
- for i, (batch_id, x0, y0, x1, y1, cls_id, score) in enumerate(outputs):
- image = ori_images[int(batch_id)]
- box = np.array([x0, y0, x1, y1])
- box -= np.array(dwdh * 2)
- box /= ratio
- box = box.round().astype(np.int32).tolist()
- cls_id = int(cls_id)
- score = round(float(score), 3)
- name = names[cls_id]
- color = colors[name]
- name += ' ' + str(score)
- cv2.rectangle(image, box[:2], box[2:], color, 2)
- cv2.putText(image, name, (box[0], box[1] - 2), cv2.FONT_HERSHEY_SIMPLEX, 0.75, [225, 255, 255], thickness=2)
-
- cv2.imshow('dddd', ori_images[0])
- cv2.waitKey(0)
- cv2.destroyAllWindows()
-
结果显示:
公主做的demo界面如下:
步骤与python版一模一样,公主一句一句翻译过来的。中间遇到过很多坑,但事情太多忘记一条一条记录了,有问题随时欢迎评论区讨论,完整代码如下:
- private void btn_analyze_Click(object sender, EventArgs e){
-
- // 原始图像切割为3份,分成1024*1024
- Mat img_img = this.img_mat;
- Mat[] subImg = new Mat[3];
- int w = Width / 3;
-
- subImg[0] = img_img[0, 1024, startX, startX + w];
- subImg[1] = img_img[0, 1024, startX + w, startX + w * 2];
- subImg[2] = img_img[0, 1024, startX + w * 2, startX + rect.Width - edge * 2];
-
- // pictureBox对象“们”存为数组
- PictureBox[] picbox = new PictureBox[3] { this.pictureBox2, this.pictureBox3, this.pictureBox4 };
- for(int i = 0; i < 3; i++)
- {
- int top = 0, left = 0;
- double ratio = 0.0;
- // after resize and padding
- Mat img_copy = new Mat();
- subImg[i].CopyTo(img_copy);
-
- // letterbox实现Resize图像
- Mat imageRgb = letterbox(img_copy, imgSize, out top, out left, out ratio);
-
- // 将Mat格式图像转化为onnx模型需要的tensor张量
- Bitmap imageBmp = BitmapConverter.ToBitmap(imageRgb);
- Tensor<float> input = FastToOnnxTensor(imageBmp);
- var inputs = new List<NamedOnnxValue>
- {
- NamedOnnxValue.CreateFromTensor("images", input)
- };
-
- // 调用onnx模型,保存结果
- IDisposableReadOnlyCollection<DisposableNamedOnnxValue> results = session.Run(inputs);
-
- // 图像后处理,并可视化
- var resultsArray = results.ToArray()[0];
- Tensor<float> tensors = resultsArray.AsTensor<float>();
- var array = tensors.ToArray();
-
- Mat newMat = new Mat();
- Cv2.CvtColor(subImg[i], newMat, ColorConversionCodes.GRAY2RGB);
-
- if (array.Length > 0)
- {
- for (int j = 0; j < tensors.Dimensions[0]; j++)
- {
- int startIndex = j * tensors.Dimensions[1];
- Scalar color = new Scalar(127, 255, 0); // 灰度颜色
- int lefttop_x = (int)((array[startIndex + 1] - left) / ratio);
- int lefttop_y = (int)((array[startIndex + 2] - top) / ratio);
- int rightbottom_x = (int)((array[startIndex + 3] - left) / ratio);
- int rightbottom_y = (int)((array[startIndex + 4] - top) / ratio);
- Cv2.Rectangle(newMat, new OpenCvSharp.Point(lefttop_x, lefttop_y),
- new OpenCvSharp.Point(rightbottom_x, rightbottom_y),
- color, 2);
- Cv2.PutText(newMat, LabelMap.Labels[(int)array[startIndex + 5]],
- new OpenCvSharp.Point(lefttop_x, lefttop_y - 5),
- HersheyFonts.HersheySimplex, 0.75, color, 2);
- }
- }
- picbox[i].Image = BitmapConverter.ToBitmap(newMat);
- }
- }
-
-
- // Load表格时创建session
- private void OnnxDemo_Load(object sender, EventArgs e){
- options = SessionOptions.MakeSessionOptionWithCudaProvider(0);
-
- session = new InferenceSession(path, options);
- }
-
-
- // letterbox预处理图像
- private Mat letterbox(Mat subImg, int newSize, out int top, out int left, out double ratio)
- {
- int width = subImg.Cols;
- int height = subImg.Rows;
-
- ratio = Math.Min(((double)newSize / (double)width), ((double)newSize / (double)height));
- int[] new_unpad = { (int)(Math.Round(height * ratio)), (int)(Math.Round(width * ratio)) };
- int[] dwh = { (newSize - new_unpad[0]) / 2, (newSize - new_unpad[1]) / 2 };
-
- if (dwh != new_unpad)
- {
- Cv2.Resize(subImg, subImg, new OpenCvSharp.Size(new_unpad[1], new_unpad[0]));
- }
-
- top = (int)(Math.Round(dwh[0] - 0.1));
- int bottom = newSize - top - new_unpad[0];
- left = (int)(Math.Round(dwh[1] - 0.1));
- int right = newSize - left - new_unpad[1];
-
- Cv2.CopyMakeBorder(subImg, subImg, top, bottom, left, right, BorderTypes.Constant, Scalar.Gray);
-
- Cv2.CvtColor(subImg, subImg, ColorConversionCodes.GRAY2RGB); //Gray to RGB
-
- return subImg;
- }
-
-
- // Bitmap格式转化为Tensor张量
- public Tensor<float> FastToOnnxTensor(Bitmap source)
- {
- var floatArray = new float[source.Width * source.Height * 3];
-
- var bitmap_data = source.LockBits(new Rectangle(0, 0, source.Width, source.Height), ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
- var bitmap_bytes = new byte[Math.Abs(bitmap_data.Stride) * source.Height];
-
- Marshal.Copy(bitmap_data.Scan0, bitmap_bytes, 0, bitmap_bytes.Length);
-
- int total_pixels_count = source.Width * source.Height;
-
-
- Parallel.For(0, total_pixels_count, (p_idx, state) =>
- {
-
- var g_idx = p_idx + total_pixels_count;
- var b_idx = p_idx + total_pixels_count * 2;
-
- floatArray[p_idx] = bitmap_bytes[p_idx * 3] / 255f;//R
- floatArray[g_idx] = bitmap_bytes[p_idx * 3 + 1] / 255f;//G
- floatArray[b_idx] = bitmap_bytes[p_idx * 3 + 2] / 255f;//B
-
- });
-
- source.UnlockBits(bitmap_data);
-
- return new DenseTensor<float>(new Memory<float>(floatArray), new int[] { 1, 3, source.Height, source.Width });
- }
静态变量:
- // 静态变量,用来保存类别名称
-
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Threading.Tasks;
-
- namespace OnnxDemo
- {
- public static class LabelMap
- {
- static LabelMap()
- {
- Labels = new string[]
- {
- "bubble"
- };
- }
- public static string[] Labels { set; get; }
- }
- }
运行结果:
Nice~Nice~Nice~~~公主对结果还是灰常满意哒!!!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。