当前位置: 首页 > news >正文

51c~ONNX~合集1

我自己的原文哦~   https://blog.51cto.com/whaosoft/11608027

一、使用Pytorch进行简单的自定义图像分类 ~ONNX 推理

   图像分类是计算机视觉中的一项基本任务,涉及训练模型将图像分类为预定义类别。本文中,我们将探讨如何使用 PyTorch 构建一个简单的自定义对象分类模型,然后使用 ONNX 格式将其部署用于推理。

数据集准备

    在开始创建模型之前,准备一个标记数据集至关重要。收集要分类的不同对象类别的图像,并根据其类别将它们组织到单独的文件夹中。确保每个类别都有足够数量的图像,以避免过度拟合。

    准备如下树所示的数据集

- data- Fruits (dataset name)- train- class 1- class 2- ...- class n- val- class 1- class 2- ...- class n- test- class 1- class 2- ...- class n

    我从 kaggle 获取了水果数据集,作为示例链接在此处:

https://www.kaggle.com/datasets/shreyapmaher/fruits-dataset-images

    更改 main.py 中“train_dir”和“val_dir”中的路径名

    如果需要,初始化数据加载器并添加增强功能。

构建模型

    首先导入必要的库,包括 PyTorch。定义自定义对象分类模型的架构,通常使用卷积神经网络 (CNN)。设计网络层,包括卷积层和池化层

import torch.nn as nnclass CustomConvNet(nn.Module):def __init__(self, num_classes):super(CustomConvNet, self).__init__()self.num_classes = num_classesself.layer1 = self.conv_module(3, 16)self.layer2 = self.conv_module(16, 32)self.layer3 = self.conv_module(32, 64)self.layer4 = self.conv_module(64, 128)self.layer5 = self.conv_module(128, 256)self.gap = self.global_avg_pool(256, self.num_classes)def forward(self, x):out = self.layer1(x)out = self.layer2(out)out = self.layer3(out)out = self.layer4(out)out = self.layer5(out)out = self.gap(out)out = out.view(-1, self.num_classes)return outdef conv_module(self, in_num, out_num):return nn.Sequential(nn.Conv2d(in_num, out_num, kernel_size=3, stride=1, padding=1),nn.BatchNorm2d(out_num),nn.LeakyReLU(),nn.MaxPool2d(kernel_size=2, stride=2))def global_avg_pool(self, in_num, out_num):return nn.Sequential(nn.Conv2d(in_num, out_num, kernel_size=3, stride=1, padding=1),nn.BatchNorm2d(out_num),nn.LeakyReLU(),nn.AdaptiveAvgPool2d((1, 1)))

训练模型并进行评估

    使用随机梯度下降 (SGD) 或 Adam 优化器等技术实现训练循环,包括前向和后向传播、损失计算和梯度优化。通过跟踪损失和准确度等指标来监控训练过程。我们利用数据增强和正则化等技术来提高模型的泛化能力。

python main.py
for epoch in range(num_epochs):print("Epoch No -", epoch)model.train()running_loss = 0.0running_corrects = 0for inputs, labels in dataLoaders["train"]:# Feeding input and labels to deviceinputs = inputs.to(device, non_blocking=True)labels = labels.to(device, non_blocking=True)optimizer.zero_grad()with torch.set_grad_enabled(True):outputs = model(inputs)_, preds = torch.max(outputs,1)loss = criterion(outputs, labels)loss.backward()optimizer.step()running_loss += loss.item()* inputs.size(0)#calculate accuracyrunning_corrects += torch.sum(preds == labels.data)#scheduler stepexp_lr_scheduler.step()# Calculate average loss and acc for a epochepoch_loss = running_loss/len(train_data)epoch_acc = running_corrects.double()/len(train_data)print('Loss:{} , Acc{}'.format(epoch_loss, epoch_acc))# Saving model every five epochif (epoch%5 == 0):save_model(model,epoch)

    运行预测

    确保你是否已经更改了权重文件名、训练文件夹、predict.py 中的输入图像

python predict.py

导出为 ONNX 格式

    一旦您的模型经过训练并在验证集上表现良好,就可以将其导出为 ONNX 格式进行部署和推理。ONNX(开放神经网络交换)是一种开放标准格式,允许不同深度学习框架之间的互操作性。PyTorch 提供了将模型导出为 ONNX 的工具。

    确保你是否更改了export.py中的权重文件名

python export.py
# Now we will save this model.
import torch.onnx
torch.onnx.export(model,img,"./savedModels/custommodel.onnx",export_params=True,opset_version=10,verbose=True,              # Print verbose outputinput_names=['input'],     # Names for input tensoroutput_names=['output'])

使用 ONNX 进行推理

    加载已保存的 ONNX 模型并对新的未见过的图像进行推理。使用 ONNX 运行时库加载模型、提供输入数据并获得预测。测量推理时间并将其与 PyTorch 模型的推理时间进行比较,以评估通过 ONNX 优化实现的任何性能改进。

    确保你是否已经更改了权重文件名、训练文件夹、predict.py 中的输入图像

python onnx_inference.py
# Load the ONNX model
onnx_model = onnx.load("./savedModels/custommodel.onnx")# Create an ONNX runtime session
ort_session = onnxruntime.InferenceSession("./savedModels/custommodel.onnx")inputs = {"input": trans_img.numpy()}
outputs = ort_session.run(None, inputs)

开发板商城 天皓智联

    在本教程中,我们探索了使用 PyTorch 构建简单自定义对象分类模型的过程。我们学习了如何训练模型、评估其性能并将其导出为 ONNX 格式以进行推理。通过利用 ONNX 运行时,我们演示了如何高效地对新图像进行推理。有了这些知识,您现在可以将自定义对象分类应用于各种实际应用程序并无缝部署模型。

二、读懂 ONNX、TensorRT、OpenVINO部署框架

本文详细介绍了深度学习模型部署过程中常用的几个框架:ONNX、TensorRT 和 OpenVINO,包括它们的功能、优势以及如何将 PyTorch 模型转换为这些框架支持的格式,旨在提高模型在不同硬件平台上的推理效率和性能。文章还讨论了模型转换过程中可能遇到的问题和相应的解决方案。

这一期主要会分几个点展开:为什么我们做部署的时候要在 torch 上更进一步使用 ONNX,TensorRT,OpenVINO 等部署框架,在做 cv 模型部署的时候。我们怎么部署。在做 LLM 部署的时候,我们又会怎么做呢?

动静转换:Torch上更进一步

Torch

最核心的就是 torch 使用了动态图组网。使用动态组网的好处是。可以使用更偏向 python 语法的格式对模型进行定义。下面就给大家一个常见的网络:

import torch
import torch.nn as nn
import torch.nn.functional as Fclass SimpleNet(nn.Module):def __init__(self, input_size, hidden_size, num_classes):super(SimpleNet, self).__init__()self.fc1 = nn.Linear(input_size, hidden_size)self.fc2 = nn.Linear(hidden_size, hidden_size)self.fc3 = nn.Linear(hidden_size, num_classes)def forward(self, x):x = F.relu(self.fc1(x))x = F.relu(self.fc2(x))x = self.fc3(x)return x# 示例使用
input_size = 784  # 例如,对于MNIST数据集
hidden_size = 128
num_classes = 10model = SimpleNet(input_size, hidden_size, num_classes)
print(model)

大家很容易开心的写出这样代码,但问题是,使用动态图模式。不可避免的带来了一系列问题。对于一个动态图来说,面临了以下三点问题:

性能:

  • 动态图在每次执行时都需要重新构建计算图,这可能导致额外的开销。
  • 静态图只需构建一次,然后可以重复高效执行。
  1. 优化难度:
  • 动态图难以进行全局优化,因为图结构在运行时可能会改变。
  • 静态图允许更多的编译时优化,如内存分配优化、算子融合等。
  1. 内存使用:
  • 动态图可能需要更多的运行时内存,因为它需要保持 Python 解释器和相关对象的活跃状态。
  • 静态图可以更有效地管理内存,尤其是在推理阶段。

所以从 torch 开始,我们第一步要做的就是动转静。拿到静态图才能更好的做整体性能上的优化!

美好的愿景:ONNX

ONNX,全称 Open Neural Network Exchange,是人工智能领域中一个引人入胜的故事。它的诞生源于一个美好的愿景:在纷繁复杂的深度学习世界中架起一座沟通的桥梁。

2017 年的硅谷,各大科技巨头都在人工智能领域奋力拼搏。Facebook(现在的Meta)和 Microsoft 这两个看似竞争对手的公司,却因为一个共同的梦想走到了一起。他们希望打破AI框架之间的壁垒,让不同平台上训练的模型能够自由迁移。就这样,ONNX 项目应运而生。

听起来 ONNX 是不同模型间完美的桥梁,最后聚合到 ONNX 完成推理是很开心和能接受的事情。但是听起来越完美的事情就面临越多的问题,首先是对 ONNX 来说。ONNX 模型在某些情况下可能比原生框架的模型运行得慢。这主要是因为 ONNX 作为一个中间表示,可能无法充分利用特定硬件或框架的优化特性。想象一下,它就像是一个通用翻译器,虽然能够让不同语言的人交流,但可能会损失一些语言中的微妙之处和效率。

除此之外,AI 领域发展的太快。ONNX 并不一定能很好的表示 torch 中各种各样的算子,导致模型转换成 ONNX 失败。谈回之前简单的网络,我们如何把它转换成 ONNX 形式呢?请看:

# 将模型转换为ONNX格式
import torch.onnx# 创建一个示例输入张量
dummy_input = torch.randn(1, input_size)# 指定ONNX文件的输出路径
output_path = "simple_net.onnx"# 导出模型到ONNX
torch.onnx.export(model,               # 要转换的模型dummy_input,         # 模型的输入样例output_path,         # 输出的ONNX文件路径export_params=True,  # 存储训练好的参数权重opset_versinotallow=11,    # ONNX算子集版本do_constant_folding=True,  # 是否执行常量折叠优化input_names=['input'],   # 输入节点的名称output_names=['output'], # 输出节点的名称dynamic_axes={'input' : {0 : 'batch_size'},    # 批处理维度动态'output' : {0 : 'batch_size'}})print(f"Model has been converted to ONNX and saved as {output_path}")

厂家的秘方:OpenVINO、TensorRT

不同厂家都有自己的推理秘制配方:推理引擎。这种趋势反映了 AI 领域的激烈竞争和快速创新。每家公司都希望在这场技术革命中占据有利地位,而自研推理引擎成为了关键战略。

这种做法的核心原因在于硬件差异化和性能优化。不同公司拥有各自独特的硬件架构,如英特尔的 CPU、NVIDIA 的 GPU 或谷歌的 TPU。为了充分发挥这些硬件的潜力,定制化的推理引擎成为必然选择。这些引擎能够针对特定硬件进行深度优化,实现最佳的性能和效率。这其中,我将为大家简单介绍两种。分别是 OpenVINO 和 TensorRT。

(一)OpenVINO

图片

OpenVINO

让我们先将目光投向 OpenVINO。它的故事始于英特尔的实验室,在那里,一群充满激情的工程师梦想着如何让人工智能的力量触手可及。2018 年,OpenVINO 正式诞生,其名字中的"VINO"代表"Visual Inference and Neural network Optimization",寓意着它要为视觉智能和神经网络优化开辟一条康庄大道。

OpenVINO 可在英特尔®硬件上扩展计算机视觉和非视觉工作负载,从而最大限度地提高性能。它通过从边缘到云的高性能,人工智能和深度学习推理来加速应用程序。

关于OpenVINO的模型转换

import subprocess
import sysdef convert_onnx_to_openvino(onnx_model_path, output_dir):cmd = [sys.executable,  # 使用当前Python解释器"-m", "mo",  # 调用model optimizer"--input_model", onnx_model_path,"--output_dir", output_dir,"--data_type", "FP32"]subprocess.run(cmd, check=True)print(f"Model has been converted to OpenVINO IR format and saved in {output_dir}")# 使用示例
onnx_model_path = "simple_net.onnx"
output_dir = "openvino_model"convert_onnx_to_openvino(onnx_model_path, output_dir)

这个转换过程和 ONNX 很像,在 OpenVINO 具体执行流程里分为反序列化,输入定义和前向执行几方面。

(二)TensorRT

图片

TensorRT

与此同时,在硅谷的另一端,NVIDIA 的工程师们也在编织着自己的 AI 梦想。2017 年,TensorRT 横空出世,它的名字中的"RT"代表"Runtime",彰显了它对高性能推理的执着追求。

TensorRT 就像是一位技艺精湛的魔法师,它能够将庞大复杂的神经网络模型变成小巧高效的推理引擎。它的法术可以让模型在 NVIDIA 的 GPU 上飞驰,实现令人瞠目的低延迟和高吞吐量。想象一下,它就像是给AI装上了火箭推进器,让智能决策的速度突破音障。

TensorRT 可用于对超大规模数据中心,嵌入式平台或自动驾驶平台进行推理加速。TensorRT 现已能支持 TensorFlow,Caffe,Mxnet,Pytorch 等几乎所有的深度学习框架,将 TensorRT 和 NVIDIA 的 GPU 结合起来,能在几乎所有的框架中进行快速和高效的部署推理。但可惜,TensorRT 是一个闭源的库。

关于TensorRT模型的转换

我们一般会给 TensorRT 的模型叫为 engine,我们可以使用 trt 提供的命令行工具,trtexec进行转换

trtexec --notallow=simple_net.onnx --saveEngine=simple_net.trt --explicitBatch

推理引擎:类似的执行流程

  • OpenVINO模型部署分为两个部分:模型优化器和推理引擎。

模型优化器将训练好的模型转换为推理引擎可以识别的中间表达 –IR 文件,并在转换过程中对模型进行优化。推理引擎接受经过模型优化器转换并优化的网络模型,为 Intel 的各种计算设备提供高性能的神经网络推理运算。

  • TensorRT 模型部署也是分为两个部分:build 和 deployment 。

build:这个阶段主要完成模型转换,将不同框架的模型转换到 TensorRT。模型转换时会完成前述优化过程中的层间融合,精度校准。这一步的输出是一个针对特定 GPU 平台和网络模型的优化过的 TensorRT 模型,这个 TensorRT 模型可以序列化存储到磁盘或内存中。存储到磁盘中的文件称之为 plan file。deployment:将上面一个步骤中的 plan 文件首先反序列化,并创建一个 runtime engine,然后就可以输入数据(比如测试集或数据集之外的图片),然后输出分类向量结果或检测结果。

写在最后

模型部署以加速为最终目的,首先就会抛弃易用性。这里特指静态图,在固定的范围内做极致的优化。除了模型上的优化,不同硬件厂商更会在贴近不同硬件上做各种底层上的优化。以获得在特定芯片上极致的性能。请期待后续部署教程吧~

三、onnxruntime部署YOLOv8分割模型详细教程

本文将详细介绍如何使用onnxruntime框架来部署YOLOv8分割模型,为了方便理解,代码采用Python实现。

0. 引言

我之前写的文章《基于YOLOv8分割模型实现垃圾识别》介绍了如何使用​​YOLOv8​​​分割模型来实现垃圾识别,主要是介绍如何用自定义的数据集来训练​​YOLOv8​​​分割模型。那么训练好的模型该如何部署呢?​​YOLOv8​​分割模型相比检测模型多了一个实例分割的分支,部署的时候还需要做一些后处理操作才能得到分割结果。

本文将详细介绍如何使用​​onnxruntime​​​框架来部署​​YOLOv8​​​分割模型,为了方便理解,代码采用​​Python​​实现。

1. 准备工作

  • 「安装onnxruntime」​onnxruntime​​​分为​​GPU​​​版本和​​CPU​​​版本,均可以通过​​pip​​直接安装:
pip install onnxruntime-gpu  #安装GPU版本  pip install onnxruntime  #安装CPU版本

「注意:」 ​​GPU​​​版本和​​CPU​​​版本建议只选其中一个安装,否则默认会使用​​CPU​​版本。

  • 「下载YOLOv8分割模型权重」​Ultralytics​​​官方提供了用​​COCO​​​数据集训练的模型权重,我们可以直接从官方网站​​https://docs.ultralytics.com/tasks/segment/​​​下载使用,本文使用的模型为​​yolov8m-seg.pt​​。

  • 「转换onnx模型」调用下面的命令可以把​​YOLOv8m-seg.pt​​​模型转换为​​onnx​​格式的模型:
yolo task=segment mode=export model=yolov8m-seg.pt format=onnx

转换成功后得到的模型为​​yolov8m-seg.onnx​​。

2. 模型部署

2.1 加载onnx模型

首先导入​​onnxruntime​​​包,然后调用其​​API​​加载模型即可:

import onnxruntime as ort  session = ort.InferenceSession("yolov8m-seg.onnx", providers=["CUDAExecutionProvider"])

因为我使用的是​​GPU​​​版本的​​onnxruntime​​​,所以​​providers​​​参数设置的是​​"CUDAExecutionProvider"​​​;如果是​​CPU​​​版本,则需设置为​​"CPUExecutionProvider"​​。

模型加载成功后,我们可以查看一下模型的输入、输出层的属性:

for input in session.get_inputs():  print("input name: ", input.name)  print("input shape: ", input.shape)  print("input type: ", input.type)  for output in session.get_outputs():  print("output name: ", output.name)  print("output shape: ", output.shape)  print("output type: ", output.type)

结果如下:

input name:  images  
input shape:  [1, 3, 640, 640]  
input type:  tensor(float)  
output name:  output0  
output shape:  [1, 116, 8400]  
output type:  tensor(float)  
output name:  output1  
output shape:  [1, 32, 160, 160]  
output type:  tensor(float)

从上面的打印信息可以知道,模型有一个尺寸为​​[1, 3, 640, 640]​​​的输入层和两个尺寸分别为​​[1, 116, 8400]​​​和​​[1, 32, 160, 160]​​的输出层。

2.2 数据预处理

数据预处理采用​​OpenCV​​​和​​Numpy​​实现,首先导入这两个包

import cv2  
import numpy as np

用​​OpenCV​​​读取图片后,把数据按照​​YOLOv8​​的要求做预处理

​image = cv2.imread("soccer.jpg") image_height, image_width, _ = image.shape input_tensor = prepare_input(image, model_width, model_height) print("input_tensor shape: ", input_tensor.shape)​

其中预处理函数​​prepare_input​​的实现如下:

def prepare_input(bgr_image, width, height):  image = cv2.cvtColor(bgr_image, cv2.COLOR_BGR2RGB)  image = cv2.resize(image, (width, height)).astype(np.float32)  image = image / 255.0  image = np.transpose(image, (2, 0, 1))  input_tensor = np.expand_dims(image, axis=0)  return input_tensor

处理流程如下:

1. 把OpenCV读取的BGR格式的图片转换为RGB格式;  
2. 把图片resize到模型输入尺寸640x640;  
3. 对像素值除以255做归一化操作;  
4. 把图像数据的通道顺序由HWC调整为CHW;  
5. 扩展数据维度,将数据的维度调整为NCHW。

经过预处理后,输入数据​​input_tensor​​​的维度变为​​[1, 3, 640, 640]​​,与模型的输入尺寸一致。

2.3 模型推理

输入数据准备好以后,就可以送入模型进行推理:

outputs = session.run(None, {session.get_inputs()[0].name: input_tensor})

前面我们打印了模型的输入输出属性,可以知道模型有两个输出分支,其中一个​​output0​​​是目标检测分支,另一个​​output1​​则是实例分割分支,这里打印一下它们的尺寸看一下

#squeeze函数是用于删除shape中为1的维度,对output0做transpose操作是为了方便后续操作  
output0 = np.squeeze(outputs[0]).transpose()  
output1 = np.squeeze(outputs[1])  
print("output0 shape:", output0.shape)  
print("output1 shape:", output1.shape)

结果如下:

output0 shape: (8400, 116)  
output1 shape: (32, 160, 160)

处理后目标检测分支的维度为​​[8400, 116]​​​,表示模型总共可以检测出​​8400​​​个目标(大部分是无效的目标),每个目标包含​​116​​​个参数。刚接触​​YOLOv8​​​分割模型的时候可能会对​​116​​​这个数字感到困惑,这里有必要解释一下:每个目标的参数包含​​4​​​个坐标属性(​​x,y,w,h​​​)、​​80​​​个类别置信度和​​32​​​个实例分割参数,所以总共是​​116​​​个参数。实例分割分支的维度为​​[32, 160, 160]​​​,其中第一个维度​​32​​​与目标检测分支中的​​32​​​个实例分割参数对应,后面两个维度则由模型输入的宽和高除以​​4​​​得到,本文所用的模型输入宽和高都是​​640​​​,所以这两个维度都是​​160​​。

2.4 后处理

首先把目标检测分支输出的数据分为两个部分,把实例分割相关的参数从中剥离。

boxes = output0[:, 0:84]  
masks = output0[:, 84:]  
print("boxes shape:", boxes.shape)  
print("masks shape:", masks.shape)
boxes shape: (8400, 84)  
masks shape: (8400, 32)

然后实例分割这部分数据​​masks​​​要与模型的另外一个分支输出的数据​​output1​​​做矩阵乘法操作,在这之前要把​​output1​​的维度变换为二维。

output1 = output1.reshape(output1.shape[0], -1)  
masks = masks @ output1  
print("masks shape:", masks.shape)
masks shape: (8400, 25600)

做完矩阵乘法后,就得到了​​8400​​​个目标对应的实例分割掩码数据​​masks​​​,可以把它与目标检测的结果​​boxes​​拼接到一起。

detections = np.hstack([boxes, masks])  
print("detections shape:", detections.shape)
detections shape: (8400, 25684)

到这里读者应该就能理解清楚了,​​YOLOv8​​​模型总共可以检测出​​8400​​​个目标,每个目标的参数包含​​4​​​个坐标属性(​​x,y,w,h​​​)、​​80​​​个类别置信度和一个​​160x160=25600​​大小的实例分割掩码。

由于​​YOLOv8​​​模型检测出的​​8400​​个目标中有大量的无效目标,所以先要通过置信度过滤去除置信度低于阈值的目标,对于满足置信度满足要求的目标还需要通过非极大值抑制(NMS)操作去除重复的目标。

objects = []  
for row in detections:  prob = row[4:84].max()  if prob < 0.5:  continue  class_id = row[4:84].argmax()  label = COCO_CLASSES[class_id]  xc, yc, w, h = row[:4]  // 把x1, y1, x2, y2的坐标恢复到原始图像坐标  x1 = (xc - w / 2) / model_width * image_width  y1 = (yc - h / 2) / model_height * image_height  x2 = (xc + w / 2) / model_width * image_width  y2 = (yc + h / 2) / model_height * image_height  // 获取实例分割mask  mask = get_mask(row[84:25684], (x1, y1, x2, y2), image_width, image_height)  // 从mask中提取轮廓  polygon = get_polygon(mask, x1, y1)  objects.append([x1, y1, x2, y2, label, prob, polygon, mask])  // NMS  
objects.sort(key=lambda x: x[5], reverse=True)  
results = []  
while len(objects) > 0:  results.append(objects[0])  objects = [object for object in objects if iou(object, objects[0]) < 0.5]

这里重点讲一下获取实例分割掩码的过程。

前面说了每个目标对应的实例分割掩码数据大小为​​160x160​​​,但是这个尺寸是对应整幅图的掩码。对于单个目标来说,还要从这个​​160x160​​​的掩码中去截取属于自己的掩码,截取的范围由目标的​​box​​​决定。上面的代码得到的​​box​​​是相对于原始图像大小,截取掩码的时候需要把​​box​​​的坐标转换到相对于​​160x160​​​的大小,截取完后再把这个掩码的尺寸调整回相对于原始图像大小。截取到​​box​​​大小的数据后,还需要对数据做​​sigmoid​​​操作把数值变换到​​0​​​到​​1​​​的范围内,也就是求这个​​box​​​范围内的每个像素属于这个目标的置信度。最后通过阈值操作,置信度大于​​0.5​​的像素被当做目标,否则被认为是背景。

具体实现的代码如下:

def get_mask(row, box, img_width, img_height):  mask = row.reshape(160, 160)  x1, y1, x2, y2 = box  // box坐标是相对于原始图像大小,需转换到相对于160*160的大小  mask_x1 = round(x1 / img_width * 160)  mask_y1 = round(y1 / img_height * 160)  mask_x2 = round(x2 / img_width * 160)  mask_y2 = round(y2 / img_height * 160)  mask = mask[mask_y1:mask_y2, mask_x1:mask_x2]  mask = sigmoid(mask)  // 把mask的尺寸调整到相对于原始图像大小  mask = cv2.resize(mask, (round(x2 - x1), round(y2 - y1)))  mask = (mask > 0.5).astype("uint8") * 255  return mask

这里需要注意的是,160x160是相对于模型输入尺寸为640x640来的,如果模型输入是其他尺寸,那么上面的代码需要做相应的调整。

如果需要检测的是下面这个图片:

通过上面的代码可以得到最左边那个人的分割掩码为

但是我们需要的并不是这样一张图片,而是需要用于表示这个目标的轮廓,这可以通过​​OpenCV​​​的​​findContours​​​函数来实现。​​findContours​​​函数返回的是一个用于表示该目标的点集,然后我们可以在原始图像中用​​fillPoly​​函数画出该目标的分割结果。

全部目标的检测与分割结果如下:

3. 一点其他的想法

从前面的部署过程可以知道,做后处理的时候需要对实例分割的数据做矩阵乘法、​​sigmoid​​​激活、维度变换等操作,实际上这些操作也可以在导出模型的时候集成到​​onnx​​模型中去,这样就可以简化后处理操作。

首先需要修改​​ultralytics​​​代码仓库中​​ultralytics/nn/modules/head.py​​​文件的代码,把​​Segment​​​类​​Forward​​函数最后的代码修改为:

if self.export:  output1 = p.reshape(p.shape[0], p.shape[1], -1)  boxes = x.permute(0, 2, 1)  masks = torch.sigmoid(mc.permute(0, 2, 1) @ output1)  out = torch.cat([boxes, masks], dim=2)  return out  
else:  return (torch.cat([x[0], mc], 1), (x[1], mc, p))

然后修改​​ultralytics/engine/exporter.py​​​文件中​​torch.onnx.export​​​的参数,把模型的输出数量改为​​1​​个。

代码修改完成后,执行命令​​pip install -e '.[dev]'​​​使之生效,然后再重新用​​yolo​​​命令导出模型。用​​netron​​​工具可以看到模型只有一个​​shape​​​为​​[1,8400,25684]​​的输出。

这样在后处理的时候就可以直接去解析​​box​​​和​​mask​​​了,并且​​mask​​​的数据不需要进行​​sigmoid​​激活。

参考资料

1.How to implement instance segmentation using YOLOv8 neural network

2.https://github.com/AndreyGermanov/yolov8_segmentation_python

四、

相关文章:

51c~ONNX~合集1

我自己的原文哦~ https://blog.51cto.com/whaosoft/11608027 一、使用Pytorch进行简单的自定义图像分类 ~ONNX 推理 图像分类是计算机视觉中的一项基本任务&#xff0c;涉及训练模型将图像分类为预定义类别。本文中&#xff0c;我们将探讨如何使用 PyTorch 构建一个简单的自定…...

线下陪玩系统架构与功能分析

2015工作至今&#xff0c;10年资深全栈工程师&#xff0c;CTO&#xff0c;擅长带团队、攻克各种技术难题、研发各类软件产品&#xff0c;我的代码态度&#xff1a;代码虐我千百遍&#xff0c;我待代码如初恋&#xff0c;我的工作态度&#xff1a;极致&#xff0c;责任&#xff…...

海康工业相机的应用部署不是简简单单!?

作者&#xff1a;SkyXZ CSDN&#xff1a;SkyXZ&#xff5e;-CSDN博客 博客园&#xff1a;SkyXZ - 博客园 笔者使用的设备及环境&#xff1a;WSL2-Ubuntu22.04MV-CS016-10UC 不会吧&#xff1f;不会吧&#xff1f;不会还有人拿到海康工业相机还是一脸懵叭&#xff1f;不会还有人…...

SAP POC 项目完工进度 - 收入确认方式【工程制造行业】【新准则下工程项目收入确认】

1. SAP POC收入确认基础概念 1.1 定义与原则 SAP POC&#xff08;Percentage of Completion&#xff09;收入确认方式是一种基于项目完工进度来确认收入的方法。其核心原则是根据项目实际完成的工作量或成本投入占预计总工作量或总成本的比例&#xff0c;来确定当期应确认的收…...

【Elasticsearch 】 聚合分析:聚合概述

&#x1f9d1; 博主简介&#xff1a;CSDN博客专家&#xff0c;历代文学网&#xff08;PC端可以访问&#xff1a;https://literature.sinhy.com/#/?__c1000&#xff0c;移动端可微信小程序搜索“历代文学”&#xff09;总架构师&#xff0c;15年工作经验&#xff0c;精通Java编…...

【算法】二分

二分 1.二分查找1.在排序数组中查找元素的第一个和最后一个位置2.牛可乐和魔法封印3.A-B 数对4.烦恼的高考志愿 2.二分答案1.木材加工2.砍树3.跳石头 1.二分查找 当我们的解具有二段性&#xff08;根据最终答案所在的位置判断是否具有二段性&#xff09;时&#xff0c;就可以使…...

如何将自己本地项目开源到github上?

环境&#xff1a; LLMB项目 问题描述&#xff1a; 如何将自己本地项目开源到github上&#xff1f; 解决方案&#xff1a; 步骤 1: 准备本地项目 确保项目整洁 确认所有的文件都在合适的位置&#xff0c;并且项目的 README.md 文件已经完善。检查是否有敏感信息&#xff0…...

编辑器Vim基本模式和指令 --【Linux基础开发工具】

文章目录 一、编辑器Vim 键盘布局二、Linux编辑器-vim使用三、vim的基本概念正常/普通/命令模式(Normal mode)插入模式(Insert mode)末行模式(last line mode) 四、vim的基本操作五、vim正常模式命令集插入模式从插入模式切换为命令模式移动光标删除文字复制替换撤销上一次操作…...

Scade 表达式 - 使用索引的迭代器

Scade 表达式中的 map, fold, mapfold&#xff0c;会对输入数组参数中的元素逐个作处理&#xff0c;不需要数组元素的索引信息。若在处理数组元素时&#xff0c;需要数组元素相应的索引信息&#xff0c;则可使用迭代器算子 mapi, foldi, mapfoldi。 mapi 算子 mapi 算子的行为…...

K8s学习

Kubernetes 1. Kubernetes介绍 1.1 应用部署方式演变 在部署应用程序的方式上&#xff0c;主要经历了三个时代&#xff1a; 传统部署&#xff1a;互联网早期&#xff0c;会直接将应用程序部署在物理机上 优点&#xff1a;简单&#xff0c;不需要其它技术的参与 缺点&#xf…...

面试--你的数据库中密码是如何存储的?

文章目录 三种分类使用 MD5 加密存储加盐存储Base64 编码:常见的对称加密算法常见的非对称加密算法https 传输加密 在开发中需要存储用户的密码&#xff0c;这个密码一定是加密存储的&#xff0c;如果是明文存储那么如果数据库被攻击了&#xff0c;密码就泄露了。 我们要对数据…...

微服务学习-快速搭建

1. 速通版 1.1. git clone 拉取项目代码&#xff0c;导入 idea 中 git clone icoolkj-microservices-code: 致力于搭建微服务架构平台 1.2. git checkout v1.0.1版本 链接地址&#xff1a;icoolkj-microservices-code 标签 - Gitee.com 2. 项目服务结构 3. 实现重点步骤 …...

兼职全职招聘系统架构与功能分析

2015工作至今&#xff0c;10年资深全栈工程师&#xff0c;CTO&#xff0c;擅长带团队、攻克各种技术难题、研发各类软件产品&#xff0c;我的代码态度&#xff1a;代码虐我千百遍&#xff0c;我待代码如初恋&#xff0c;我的工作态度&#xff1a;极致&#xff0c;责任&#xff…...

【云岚到家】-day03-门户缓存实现实战

【云岚到家】-day03-门户缓存实现实战 1.定时任务更新缓存 1.1 搭建XXL-JOB环境 1.1.1 分布式调度平台XXL-JOB介绍 对于开通区域列表的缓存数据需要由定时任务每天凌晨更新缓存&#xff0c;如何实现定时任务呢&#xff1f; 1.使用jdk提供的Timer定时器 示例代码如下&#xf…...

Ubuntu 24.04 LTS 开启 SMB 服务,并通过 windows 访问

Ubuntu 24.04 LTS 背景资料 Ubuntu服务器折腾集Ubuntu linux 文件权限Ubuntu 空闲硬盘挂载到 文件管理器的 other locations Ubuntu开启samba和window共享文件 Ubuntu 配置 SMB 服务 安装 Samba 确保 Samba 已安装。如果未安装&#xff0c;运行以下命令进行安装&#xff…...

“AI人工智能内容辅助创作平台:让创意不再“卡壳”

在如今这个信息爆炸的时代&#xff0c;内容创作成了每个人的“必修课”。无论是自媒体大V、文案策划&#xff0c;还是普通学生写作文&#xff0c;大家都会遇到一个让人抓狂的问题——“创意枯竭”。有时候&#xff0c;脑袋里空空如也&#xff0c;一个字都写不出来&#xff0c;那…...

mac 安装 node

brew versions node // 安装 node brew versions node14 // 安装指定版本 卸载node: sudo npm uninstall npm -g sudo rm -rf /usr/local/lib/node /usr/local/lib/node_modules /var/db/receipts/org.nodejs.* sudo rm -rf /usr/local/include/node /Users/$USER/.npm su…...

VUE之Router使用及工作模式

1、路由的使用 【两个注意点】 1)路由组件通常放在pages 或 views文件夹,一般组件通常放在components文件夹。 2)通过点击导航,视觉效果上"消失"了的路由组件,默认是被"卸载"掉的,需要的时候再去挂载。 // 创建一个路由器,并暴露出去// 第一步:…...

day25_HTML

今日内容 零、 复习昨日 一、HTML 零、 复习昨日 一、Web开发 前端 HTML ,页面展现CSS , 样式JS (JavaScript) , 动起来 二、HTML 2.1 HTML概念 ​ 网页&#xff0c;是网站中的一个页面&#xff0c;通常是网页是构成网站的基本元素&#xff0c;是承载各种网站应用的平台。通俗…...

(开源)基于Django+Yolov8+Tensorflow的智能鸟类识别平台

1 项目简介&#xff08;开源地址在文章结尾&#xff09; 系统旨在为了帮助鸟类爱好者、学者、动物保护协会等群体更好的了解和保护鸟类动物。用户群体可以通过平台采集野外鸟类的保护动物照片和视频&#xff0c;甄别分类、实况分析鸟类保护动物&#xff0c;与全世界各地的用户&…...

【AI日记】25.01.20

【AI论文解读】【AI知识点】【AI小项目】【AI战略思考】【AI日记】【读书与思考】 AI kaggle 比赛&#xff1a;Forecasting Sticker Sales 读书 书名&#xff1a;自由宪章阅读原因&#xff1a;作者哈耶克&#xff0c;诺贝尔经济学奖得主&#xff0c;之前读过他的 《通往奴役…...

基于机器学习的用户健康风险分类及预测分析

完整源码项目包获取→点击文章末尾名片&#xff01; 背景描述 在这个日益注重健康与体能的时代&#xff0c;健身已成为许多人追求健康生活的重要组成部分。 本数据集包含若干健身房会员的详细信息&#xff0c;包括年龄、性别、体重、身高、心率、锻炼类型、身体脂肪比例等多项关…...

AI生成内容——JavaScript中的Promise、async和wait

一、Promise *1. 概念&#xff1a; Promise 是 JavaScript 中处理异步操作的一种对象&#xff0c;它表示一个异步操作的最终完成&#xff08;或失败&#xff09;及其结果值。一个 Promise 对象处于以下三种状态之一&#xff1a; Pending&#xff08;进行中&#xff09;&#…...

Java基于SSM框架的社区团购系统小程序设计与实现(附源码,文档,部署)

Java基于SSM框架的社区团购系统小程序设计与实现 博主介绍&#xff1a;✌程序猿徐师兄、8年大厂程序员经历。全网粉丝15w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f4…...

Git原理与应用(三)【远程操作 | 理解分布式 | 推送拉取远程仓库 | 标签管理】

Git 理解分布式版本控制系统远程仓库新建远程仓库克隆远程仓库向远程仓库推送配置Git忽略特殊文件 标签管理理解标签创建标签操作标签删除标签 理解分布式版本控制系统 我们⽬前所说的所有内容&#xff08;工作区&#xff0c;暂存区&#xff0c;版本库等等&#xff09;&#x…...

【esp32小程序】小程序篇02——连接git

一、创建仓库 进入gitee官网&#xff0c;登录&#xff08;如果没有gitee账号的就自行注册一下&#xff09;。 点击号-->新建仓库 填写好必填信息&#xff0c;然后点击“创建” 二、微信开发者工具配置 在微信开发者工具打开我们的项目。按下面的步骤依次点击 三、验证 点…...

MongoDB基本操作

一、实验目的 1. 熟悉MongoDB的基本操作&#xff0c;包括CRUD&#xff08;增加、读取、更新、删除&#xff09;。 2. 理解MongoDB的文档型数据库特性和Shell的使用。 3. 培养学生通过命令行操作数据库的能力。 4. 强化数据库操作的实际应用能力。 二、实验环境准备 1.…...

Brooks MagnaTran LEAP User Manual 指导半导体机械手

Brooks MagnaTran LEAP User Manual 指导半导体机械手...

【Red Hat8】:搭建DHCP服务器

1、新建挂载文件 2、挂载 3、关闭防火墙 4、搭建yum源 &#xff08;搭建的时候用vim 自行定义文件名.repo或者是vi 自行定义文件名.repo&#xff09; 5、安装dhcp-server 6、复制模板文件 dhcpd.conf 是DHCP服务的配置文件&#xff0c;DHCP服务所有参数都是通过修改dhcpd.co…...

JupyterLab 安装以及部分相关配置

安装 JupyterLab pip install jupyter启动 JupyterLab jupyter lab [--port <指定的端口号>] [--no-browser] # --port 指定端口 # --no-browser 启动时不打开浏览器安装中文 首先安装中文包 pip install jupyterlab-language-pack-zh-CN安装完成后重启 JupyterLab 选…...

深圳桂湾公园的花海

工作日的午休时间我经常骑行到桂湾公园&#xff0c;时不时都能碰上一些阿姨问&#xff1a;小伙子你知道桂湾公园的花海在哪里吗&#xff1f;我找了半天了哈。我发现不少找花海的人是从桂湾地铁或前湾地铁下车&#xff0c;然后在偌大的桂湾公园找寻。其实只要定位前海紫荆园就好…...

寒假刷题Day10

一、220. 存在重复元素 III 两种解法&#xff1a;并没有弄懂&#xff0c;待复盘 class Solution { public:bool containsNearbyAlmostDuplicate(vector<int>& nums, int k, int t) {set<long> st;for (int i 0; i < nums.size(); i) {auto lb st.lower_…...

【Java-图片存储方案】

Java功能相关文章 一、Minio存储大体量图片 上传到Minio指定路径&#xff0c;前端预览时,需要生成临时访问凭证的URL import io.minio.MinioClient; import io.minio.errors.MinioException; import io.minio.http.Method; import io.minio.GetPresignedObjectUrlArgs; impo…...

机器人传动力系统介绍

以下是对机器人驱动系统的分析、最新科技应用以及世界顶级公司机器人型号使用的技术&#xff1a; 机器人驱动系统分析 液压驱动&#xff1a;利用液体压力来传递动力&#xff0c;通过液压泵将液压油从油箱抽出&#xff0c;送至液压缸&#xff0c;推动活塞运动&#xff0c;进而…...

DDD - 微服务落地的技术实践

文章目录 Pre概述如何发挥微服务的优势怎样提供微服务接口原则微服务的拆分与防腐层的设计 去中心化的数据管理数据关联查询的难题Case 1Case 2Case 3 总结 Pre DDD - 软件退化原因及案例分析 DDD - 如何运用 DDD 进行软件设计 DDD - 如何运用 DDD 进行数据库设计 DDD - 服…...

《Vue3 十》Vue 底层原理

命令式编程和声明式编程&#xff1a; 以计时器为例&#xff1a; // 原生 JavaScript 实现计数器&#xff0c;是命令式编程 <div><h1>当前数字&#xff1a;<span class"count"></span></h1><button class"add" click&qu…...

GMM高斯混合聚类算法(Matlab)

目录 效果一览基本介绍程序设计参考资料 效果一览 基本介绍 GMM高斯混合聚类算法 matlab2023b语言&#xff0c;一键出图&#xff0c;直接运行 1.代码注释清晰&#xff0c;自行解读容易。 2…输出图例如图所示包括&#xff1a;聚类图(聚类结果图)&#xff0c;协方差矩阵类型…...

【Leetcode 每日一题】2266. 统计打字方案数

问题背景 Alice 在给 Bob 用手机打字。数字到字母的 对应 如下图所示。 为了 打出 一个字母&#xff0c;Alice 需要 按 对应字母 i i i 次&#xff0c; i i i 是该字母在这个按键上所处的位置。 比方说&#xff0c;为了按出字母 ‘s’ &#xff0c;Alice 需要按 ‘7’ 四次…...

多线程杂谈:惊群现象、CAS、安全的单例

引言 本文是一篇杂谈&#xff0c;帮助大家了解多线程可能会出现的面试题。 目录 引言 惊群现象 结合条件变量 CAS原子操作&#xff08;cmp & swap&#xff09; 线程控制&#xff1a;两个线程交替打印奇偶数 智能指针线程安全 单例模式线程安全 最简单的单例&…...

Nginx调优

Nginx 是一个高性能的反向代理服务器和负载均衡器&#xff0c;在处理大量并发请求时表现出色。但是&#xff0c;随着系统负载的增加&#xff0c;Nginx 的性能可能受到多方面的影响&#xff0c;因此进行适当的调优至关重要。以下是 Nginx 调优的几个方向和关键点&#xff1a; 1…...

自定义UITableViewCell

很多时候&#xff0c;我们是不能直接使用系统自带的UITableViewCell&#xff0c;因为自带的比较简单只有一个UIImageView和两个UILabel&#xff0c;假设需要多个UIImageView或者两个以上UILabel&#xff0c;那就需要自定义了。本文就实现如何自定义UITableViewCell。 假设我们现…...

Java 基于微信小程序的原创音乐小程序设计与实现(附源码,部署,文档)

大家好&#xff0c;我是stormjun&#xff0c;今天为大家带来的是Java实战项目-基于微信小程序的原创音乐小程序设计与实现。该系统采用 Java 语言 开发&#xff0c;MySql 作为数据库&#xff0c;系统功能完善 &#xff0c;实用性强 &#xff0c;可供大学生实战项目参考使用。 博…...

MySQL —— 事务

概念 事务把组SQL语句打包成为个整体&#xff0c;在这组SQL的执行过程中&#xff0c;要么全部成功&#xff0c;要么全部失败。 这组SQL语句可以是条也可以是多条。 ACID 特性 原子性 Atomicity(原子性)&#xff1a;一个事务中的所有操作&#xff0c;要么全部成功&#xff0…...

【大模型】ChatGPT 高效处理图片技巧使用详解

目录 一、前言 二、ChatGPT 4 图片处理介绍 2.1 ChatGPT 4 图片处理概述 2.1.1 图像识别与分类 2.1.2 图像搜索 2.1.3 图像生成 2.1.4 多模态理解 2.1.5 细粒度图像识别 2.1.6 生成式图像任务处理 2.1.7 图像与文本互动 2.2 ChatGPT 4 图片处理应用场景 三、文生图操…...

SpringBoot注入配置文件application.properties中的信息

问题&#xff1a;将可能会变动的配置信息硬编码在代码中&#xff0c;在修改时难以定位&#xff0c;且过于繁琐&#xff0c;怎么办&#xff1f; 解决&#xff1a;自定义application.properties配置文件中的信息&#xff0c;注意严格遵循ab的形式&#xff0c;不要加引号&#xf…...

大数据,Hadoop,HDFS的简单介绍

大数据 海量数据&#xff0c;具有高增长率、数据类型多样化、一定时间内无法使用常规软件工具进行捕捉、管理和处理的数据集 合 大数据的特征: 4V Volume : 巨大的数据量 Variety : 数据类型多样化 结构化的数据 : 即具有固定格式和有限长度的数据 半结构化的数据 : 是…...

第15章:Python TDD应对货币类开发变化(二)

写在前面 这本书是我们老板推荐过的&#xff0c;我在《价值心法》的推荐书单里也看到了它。用了一段时间 Cursor 软件后&#xff0c;我突然思考&#xff0c;对于测试开发工程师来说&#xff0c;什么才更有价值呢&#xff1f;如何让 AI 工具更好地辅助自己写代码&#xff0c;或许…...

黑马点评之导入初始项目(java)

&#xff01;&#xff01;&#xff01;由于我一开始是在网盘上下载的资源&#xff0c;后面忙活半天&#xff0c;发现代码是不完整的&#xff0c;才知道需要在github上面拉取初始代码。 然后第二点是我的本地环境是jdk21&#xff0c;但是他原本的代码为jdk8&#xff0c;所以在换…...

Erlang语言的语法糖

Erlang语言的语法糖&#xff1a;简化编程的灵活工具 Erlang是一种功能强大的编程语言&#xff0c;最初由爱立信&#xff08;Ericsson&#xff09;为电信系统开发。它以其高并发性、容错性和分布式特性而闻名&#xff0c;特别适合构建实时系统。然而&#xff0c;Erlang的语法相…...

数据库基础知识:记录、表、字段、数据类型、约束、主键、外键、规范化、索引、序列

数据库是由一个或多个有组织的数据集合组成&#xff0c;而数据库管理系统&#xff08;DBMS&#xff09;是操作数据库的软件&#xff0c;包括很多人本科上课学过的SQL Server&#xff0c;现在常用的MySQL、Postgresql等&#xff0c;用于提供数据的存储、访问、运行和维护等。学习…...