[Pytorch图像分类全流程实战]Task07:模型部署

主要讲的是ONNX-ONNX Runtime部署
我之前因为是讲torchserve服务器部署来着,但是发现本部分视频还没拍摄,torchserve服务器部署还等待催更(狗头,本部分内容原本应该如下:
在这里插入图片描述
anyway,现在只有第一个文件夹,不过由于我确实对这部分比较陌生,会看部分相关解说博客,我文中会放置相关链接。

第一个问题

什么是ONNX-ONNX Runtime,找到了一个大佬的解说(这篇文章写的很好,可看):
在这里插入图片描述
转自:模型部署之ONNX ONNXRuntime

前面看不懂的同学可以简单理解如下:
在这里插入图片描述
但是肯定不会那么简单,由于没有其他部署的对比,先姑且这么理解吧

具体内容

在这里插入图片描述

【A】部署lmageNet预训练图像分类模型

在这里插入图片描述
这个我还是过程都写,因为并不熟悉

【A】安装配置环境

  • 安装Pytorch

    !pip3 install torch torchvision --extra-index-url https://download.pytorch.org/whl/cu113
    
  • 安装工具包

    !pip install numpy pandas matplotlib tqdm opencv-python pillow onnx onnxruntime -i https://pypi.tuna.tsinghua.edu.cn/simple
    

【B】导出ONNX模型

原生Pytorch训练得到的图像分类模型,导出为ONNX格式,用于后续在ONNX Runtime推理引擎上部署。

  • 导入工具包

    import torch
    from torchvision import models
    
    # 有 GPU 就用 GPU,没有就用 CPU
    device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')
    print('device', device)
    
  • 载入lmageNet预训练图像分类模型

    model = models.resnet18(pretrained=True)
    model = model.eval().to(device)
    
    x = torch.randn(1, 3, 256, 256).to(device)
    
    output = model(x)
    
    output.shape
    #torch.Size([1, 1000])
    
  • Pytorch模型转ONNX模型

    x = torch.randn(1, 3, 256, 256).to(device)
    
    with torch.no_grad():
        torch.onnx.export(
            model,                  # 要转换的模型
            x,                      # 模型的任意一组输入
            'resnet18.onnx',        # 导出的 ONNX 文件名
            opset_version=11,       # ONNX 算子集版本
            input_names=['input'],  # 输入 Tensor 的名称(自己起名字)
            output_names=['output'] # 输出 Tensor 的名称(自己起名字)
        ) 
    

    Tensor这里是张量,相关文章:
    笔记│什么是张量(tensor)&深度学习

  • 验证oonx模型导出成功

    import onnx
    
    # 读取 ONNX 模型
    onnx_model = onnx.load('resnet18.onnx')
    
    # 检查模型格式是否正确
    onnx.checker.check_model(onnx_model)
    
    print('无报错,onnx模型载入成功')
    
  • 以可读的形式打印计算图

    print(onnx.helper.printable_graph(onnx_model.graph))
    

    这个图蛮漂亮的(数学上的好看),贴出来:

      graph torch-jit-export (
      
        %input[FLOAT, 1x3x256x256]
      
      	) initializers (
      	
      	  %fc.weight[FLOAT, 1000x512]
      	
      	  %fc.bias[FLOAT, 1000]
      	
      	  %193[FLOAT, 64x3x7x7]
      	
      	  %194[FLOAT, 64]
      	
      	  %196[FLOAT, 64x64x3x3]
      	
      	  %197[FLOAT, 64]
      	
      	  %199[FLOAT, 64x64x3x3]
      	
      	  %200[FLOAT, 64]
      	
      	  %202[FLOAT, 64x64x3x3]
      	
      	  %203[FLOAT, 64]
      	
      	  %205[FLOAT, 64x64x3x3]
      	
      	  %206[FLOAT, 64]
      	
      	  %208[FLOAT, 128x64x3x3]
      	
      	  %209[FLOAT, 128]
      	
      	  %211[FLOAT, 128x128x3x3]
      	
      	  %212[FLOAT, 128]
      	
      	  %214[FLOAT, 128x64x1x1]
      	
      	  %215[FLOAT, 128]
      	
      	  %217[FLOAT, 128x128x3x3]
      	
      	  %218[FLOAT, 128]
      	
      	  %220[FLOAT, 128x128x3x3]
      	
      	  %221[FLOAT, 128]
      	
      	  %223[FLOAT, 256x128x3x3]
      	
      	  %224[FLOAT, 256]
      	
      	  %226[FLOAT, 256x256x3x3]
      	
      	  %227[FLOAT, 256]
      	
      	  %229[FLOAT, 256x128x1x1]
      	
      	  %230[FLOAT, 256]
      	
      	  %232[FLOAT, 256x256x3x3]
      	
      	  %233[FLOAT, 256]
      	
      	  %235[FLOAT, 256x256x3x3]
      	
      	  %236[FLOAT, 256]
      	
      	  %238[FLOAT, 512x256x3x3]
      	
      	  %239[FLOAT, 512]
      	
      	  %241[FLOAT, 512x512x3x3]
      	
      	  %242[FLOAT, 512]
      	
      	  %244[FLOAT, 512x256x1x1]
      	
      	  %245[FLOAT, 512]
      	
      	  %247[FLOAT, 512x512x3x3]
      	
      	  %248[FLOAT, 512]
      	
      	  %250[FLOAT, 512x512x3x3]
      	
      	  %251[FLOAT, 512]
      	
      	) {
      	
      	  %192 = Conv[dilations = [1, 1], group = 1, kernel_shape = [7, 7], pads = [3, 3, 3, 3], strides = [2, 2]](%input, %193, %194)
      	
      	  %125 = Relu(%192)
      	
      	  %126 = MaxPool[ceil_mode = 0, kernel_shape = [3, 3], pads = [1, 1, 1, 1], strides = [2, 2]](%125)
      	
      	  %195 = Conv[dilations = [1, 1], group = 1, kernel_shape = [3, 3], pads = [1, 1, 1, 1], strides = [1, 1]](%126, %196, %197)
      	
      	  %129 = Relu(%195)
      	
      	  %198 = Conv[dilations = [1, 1], group = 1, kernel_shape = [3, 3], pads = [1, 1, 1, 1], strides = [1, 1]](%129, %199, %200)
      	
      	  %132 = Add(%198, %126)
      	
      	  %133 = Relu(%132)
      	
      	  %201 = Conv[dilations = [1, 1], group = 1, kernel_shape = [3, 3], pads = [1, 1, 1, 1], strides = [1, 1]](%133, %202, %203)
      	
      	  %136 = Relu(%201)
      	
      	  %204 = Conv[dilations = [1, 1], group = 1, kernel_shape = [3, 3], pads = [1, 1, 1, 1], strides = [1, 1]](%136, %205, %206)
      	
      	  %139 = Add(%204, %133)
      	
      	  %140 = Relu(%139)
      	
      	  %207 = Conv[dilations = [1, 1], group = 1, kernel_shape = [3, 3], pads = [1, 1, 1, 1], strides = [2, 2]](%140, %208, %209)
      	
      	  %143 = Relu(%207)
      	
      	  %210 = Conv[dilations = [1, 1], group = 1, kernel_shape = [3, 3], pads = [1, 1, 1, 1], strides = [1, 1]](%143, %211, %212)
      	
      	  %213 = Conv[dilations = [1, 1], group = 1, kernel_shape = [1, 1], pads = [0, 0, 0, 0], strides = [2, 2]](%140, %214, %215)
      	
      	  %148 = Add(%210, %213)
      	
      	  %149 = Relu(%148)
      	
      	  %216 = Conv[dilations = [1, 1], group = 1, kernel_shape = [3, 3], pads = [1, 1, 1, 1], strides = [1, 1]](%149, %217, %218)
      	
      	  %152 = Relu(%216)
      	
      	  %219 = Conv[dilations = [1, 1], group = 1, kernel_shape = [3, 3], pads = [1, 1, 1, 1], strides = [1, 1]](%152, %220, %221)
      	
      	  %155 = Add(%219, %149)
      	
      	  %156 = Relu(%155)
      	
      	  %222 = Conv[dilations = [1, 1], group = 1, kernel_shape = [3, 3], pads = [1, 1, 1, 1], strides = [2, 2]](%156, %223, %224)
      	
      	  %159 = Relu(%222)
      	
      	  %225 = Conv[dilations = [1, 1], group = 1, kernel_shape = [3, 3], pads = [1, 1, 1, 1], strides = [1, 1]](%159, %226, %227)
      	
      	  %228 = Conv[dilations = [1, 1], group = 1, kernel_shape = [1, 1], pads = [0, 0, 0, 0], strides = [2, 2]](%156, %229, %230)
      	
      	  %164 = Add(%225, %228)
      	
      	  %165 = Relu(%164)
      	
      	  %231 = Conv[dilations = [1, 1], group = 1, kernel_shape = [3, 3], pads = [1, 1, 1, 1], strides = [1, 1]](%165, %232, %233)
      	
      	  %168 = Relu(%231)
      	
      	  %234 = Conv[dilations = [1, 1], group = 1, kernel_shape = [3, 3], pads = [1, 1, 1, 1], strides = [1, 1]](%168, %235, %236)
      	
      	  %171 = Add(%234, %165)
      	
      	  %172 = Relu(%171)
      	
      	  %237 = Conv[dilations = [1, 1], group = 1, kernel_shape = [3, 3], pads = [1, 1, 1, 1], strides = [2, 2]](%172, %238, %239)
      	
      	  %175 = Relu(%237)
      	  
      	  %240 = Conv[dilations = [1, 1], group = 1, kernel_shape = [3, 3], pads = [1, 1, 1, 1], strides = [1, 1]](%175, %241, %242)
      	
      	  %243 = Conv[dilations = [1, 1], group = 1, kernel_shape = [1, 1], pads = [0, 0, 0, 0], strides = [2, 2]](%172, %244, %245)
      	
      	  %180 = Add(%240, %243)
      	
      	  %181 = Relu(%180)
      	
      	  %246 = Conv[dilations = [1, 1], group = 1, kernel_shape = [3, 3], pads = [1, 1, 1, 1], strides = [1, 1]](%181, %247, %248)
      	
      	  %184 = Relu(%246)
      	
      	  %249 = Conv[dilations = [1, 1], group = 1, kernel_shape = [3, 3], pads = [1, 1, 1, 1], strides = [1, 1]](%184, %250, %251)
      	
      	  %187 = Add(%249, %181)
      	
      	  %188 = Relu(%187)
      	
      	  %189 = GlobalAveragePool(%188)
      	
      	  %190 = Flatten[axis = 1](%189)
      	  
      	  %240 = Conv[dilations = [1, 1], group = 1, kernel_shape = [3, 3], pads = [1, 1, 1, 1], strides = [1, 1]](%175, %241, %242)
      	
      	  %243 = Conv[dilations = [1, 1], group = 1, kernel_shape = [1, 1], pads = [0, 0, 0, 0], strides = [2, 2]](%172, %244, %245)
      	
      	  %180 = Add(%240, %243)
      	
      	  %181 = Relu(%180)
      	
      	  %246 = Conv[dilations = [1, 1], group = 1, kernel_shape = [3, 3], pads = [1, 1, 1, 1], strides = [1, 1]](%181, %247, %248)
      	
      	  %184 = Relu(%246)
      	
      	  %249 = Conv[dilations = [1, 1], group = 1, kernel_shape = [3, 3], pads = [1, 1, 1, 1], strides = [1, 1]](%184, %250, %251)
      	
      	  %187 = Add(%249, %181)
      	
      	  %188 = Relu(%187)
      	
      	  %189 = GlobalAveragePool(%188)
      	
      	  %190 = Flatten[axis = 1](%189)
      	  
      	  %output = Gemm[alpha = 1, beta = 1, transB = 1](%190, %fc.weight, %fc.bias)
      
        return %output
      
      }
    
  • 使用Netron对oonx模型可视化
    网址
    在这里插入图片描述

【C】推理引擎ONNX Runtime部署-预测单张图像

使用推理引擎 ONNX Runtime,读取 onnx 格式的模型文件,对单张图像文件进行预测。

应用场景:
以下代码在需要部署的硬件上运行
只需把onnx模型文件发到部署硬件上,并安装ONNX Runtime环境,用几行代码就可以运行模型了。

  • 导入工具包
import onnxruntime
import numpy as np
import torch
  • 载入onnx模型,获取ONNX Runtime推理器

    ort_session = onnxruntime.InferenceSession('resnet18.onnx')
    
  • 构造输入,获取输出结果

    x = torch.randn(1, 3, 256, 256).numpy()
    
    x.shape
    #(1, 3, 256, 256)
    
    ##注意,输入输出张量的名称需要和 torch.onnx.export 中设置的输入输出名对应
    # onnx runtime 输入
    ort_inputs = {'input': x}
    
    # onnx runtime 输出
    ort_output = ort_session.run(['output'], ort_inputs)[0]
    
    ort_output.shape
    #(1, 1000)
    
  • 预处理

    from torchvision import transforms
    
    # 测试集图像预处理-RCTN:缩放裁剪、转 Tensor、归一化
    test_transform = transforms.Compose([transforms.Resize(256),
                                         transforms.CenterCrop(256),
                                         transforms.ToTensor(),
                                         transforms.Normalize(
                                             mean=[0.485, 0.456, 0.406], 
                                             std=[0.229, 0.224, 0.225])
                                        ])
    
  • 载入测试图像

    img_path = 'banana1.jpg'
    
    # 用 pillow 载入
    from PIL import Image
    img_pil = Image.open(img_path)
    
    print(img_pil)
    

    在这里插入图片描述

  • 运行预处理

input_img = test_transform(img_pil)

input_img.shape
#torch.Size([3, 256, 256])

input_tensor = input_img.unsqueeze(0).numpy()

input_tensor.shape
#(1, 3, 256, 256)
  • ONNX Runtime预测
# ONNX Runtime 输入
ort_inputs = {'input': input_tensor}

# ONNX Runtime 输出
pred_logits = ort_session.run(['output'], ort_inputs)[0]
pred_logits = torch.tensor(pred_logits)

pred_logits.shape
#torch.Size([1, 1000])

import torch.nn.functional as F
pred_softmax = F.softmax(pred_logits, dim=1) # 对 logit 分数做 softmax 运算

pred_softmax.shape
#torch.Size([1, 1000])
  • 柱状图可视化
import matplotlib.pyplot as plt
%matplotlib inline

plt.figure(figsize=(8,4))

x = range(1000)
y = pred_softmax.cpu().detach().numpy()[0]

ax = plt.bar(x, y, alpha=0.5, width=0.3, color='yellow', edgecolor='red', lw=3)
plt.ylim([0, 1.0]) # y轴取值范围
# plt.bar_label(ax, fmt='%.2f', fontsize=15) # 置信度数值

plt.xlabel('Class', fontsize=20)
plt.ylabel('Confidence', fontsize=20)
plt.tick_params(labelsize=16) # 坐标文字大小
plt.title(img_path, fontsize=25)

plt.show()

在这里插入图片描述
后续步骤:解析top-n预测结果、在图像上写英文和中文预测结果

【D1】推理引擎ONNX Runtime部署-预测摄像头实时画面-英文

使用 ONNX Runtime 推理引擎,载入 ImageNet 预训练图像分类 onnx 模型,预测摄像头实时画面。

注意事项:

本代码需在连接摄像头的本地运行,不能在云GPU平台运行。
在本地运行
pip install onnxruntime
安装onnx runtime,并准备好onnx模型文件。

  • 导入工具包

    import onnxruntime
    import torch
    
    import pandas as pd
    import numpy as np
    
    from PIL import Image, ImageFont, ImageDraw
    
    import matplotlib.pyplot as plt
    %matplotlib inline
    
  • 载入onnx模型,获取ONNX Runtime推理器

    ort_session = onnxruntime.InferenceSession('resnet18.onnx')
    
  • 载入lmageNet 1000图像分类标签

    df = pd.read_csv('imagenet_class_index.csv')
    idx_to_labels = {}
    for idx, row in df.iterrows():
        idx_to_labels[row['ID']] = row['class']
    
  • 图像预处理

    from torchvision import transforms
    
    # 测试集图像预处理-RCTN:缩放裁剪、转 Tensor、归一化
    test_transform = transforms.Compose([transforms.Resize(256),
                                         transforms.CenterCrop(256),
                                         transforms.ToTensor(),
                                         transforms.Normalize(
                                             mean=[0.485, 0.456, 0.406], 
                                             std=[0.229, 0.224, 0.225])
                                        ])
    
  • 预测摄像头单帧画面
    – 调用摄像头获取一帧画面

    # 导入opencv-python
    import cv2
    import time
    
    # 获取摄像头,传入0表示获取系统默认摄像头
    cap = cv2.VideoCapture(1)
    
    # 打开cap
    cap.open(0)
    
    time.sleep(1)
    
    success, img_bgr = cap.read()
        
    # 关闭摄像头
    cap.release()
    
    # 关闭图像窗口
    cv2.destroyAllWindows()
    

    – 画面转成 RGB的Pillow格式

    img_bgr.shape
    #(720, 1280, 3)
    
    img_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB) # BGR转RGB
    
    img_pil = Image.fromarray(img_rgb)
    
    print(img_pil)
    
  • 预处埋

    input_img = test_transform(img_pil)
    input_tensor = input_img.unsqueeze(0).numpy()
    
    input_tensor.shape
    #(1, 3, 256, 256)
    
  • ONNX Runtime预测

    # onnx runtime 预测
    
    # onnx runtime 输入
    ort_inputs = {'input': input_tensor}
    # onnx runtime 输出
    pred_logits = ort_session.run(['output'], ort_inputs)[0]
    pred_logits = torch.tensor(pred_logits)
    
    pred_logits.shape
    #torch.Size([1, 1000])
    
    import torch.nn.functional as F
    pred_softmax = F.softmax(pred_logits, dim=1) # 对 logit 分数做 softmax 运算
    
    pred_softmax.shape
    #torch.Size([1, 1000])
    
  • 解析top-n预测结果的类别和置信度

    n = 5
    top_n = torch.topk(pred_softmax, n) # 取置信度最大的 n 个结果
    
    confs = top_n[0].cpu().detach().numpy().squeeze()
    
    confs
    #array([0.1028311 , 0.04595807, 0.02947768, 0.02589989, 0.02027929],
    #,      dtype=float32)
    
    pred_ids = top_n[1].cpu().detach().numpy().squeeze()
    
    pred_ids
    #array([788, 918, 617, 457, 937])
    
  • 在图像上写英文

    for i in range(len(confs)):
        pred_class = idx_to_labels[pred_ids[i]]
        text = '{:<15} {:>.3f}'.format(pred_class, confs[i])
        
        # 图片,添加的文字,左上角坐标,字体,字体大小,颜色,线宽,线型
        img_bgr = cv2.putText(img_bgr, text, (50, 80 + 80 * i), cv2.FONT_HERSHEY_SIMPLEX, 2.5, (0, 0, 255), 5, cv2.LINE_AA)
        
    img_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB) # BGR转RGB
    plt.imshow(img_rgb)
    plt.show()
    

    在这里插入图片描述

  • 处理单帧画面的函数(英文)

    # 处理帧函数
    def process_frame(img):
        
        '''
        输入摄像头拍摄画面bgr-array,输出图像分类预测结果bgr-array
        '''
        
        # 记录该帧开始处理的时间
        start_time = time.time()
        
        ## 画面转成 RGB 的 Pillow 格式
        img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # BGR转RGB
        img_pil = Image.fromarray(img_rgb) # array 转 PIL
        
        ## 预处理
        input_img = test_transform(img_pil) # 预处理
        input_tensor = input_img.unsqueeze(0).numpy()
        
        ## onnx runtime 预测
        ort_inputs = {'input': input_tensor} # onnx runtime 输入
        pred_logits = ort_session.run(['output'], ort_inputs)[0] # onnx runtime 输出
        pred_logits = torch.tensor(pred_logits)
        pred_softmax = F.softmax(pred_logits, dim=1) # 对 logit 分数做 softmax 运算
        
        ## 解析top-n预测结果的类别和置信度
        top_n = torch.topk(pred_softmax, 5) # 取置信度最大的 n 个结果
        pred_ids = top_n[1].cpu().detach().numpy().squeeze() # 解析预测类别
        confs = top_n[0].cpu().detach().numpy().squeeze() # 解析置信度
        
        # 在图像上写英文
        for i in range(len(confs)):
            pred_class = idx_to_labels[pred_ids[i]]
            text = '{:<15} {:>.3f}'.format(pred_class, confs[i])
    
            # 图片,添加的文字,左上角坐标,字体,字体大小,颜色,线宽,线型
            img = cv2.putText(img, text, (50, 160 + 80 * i), cv2.FONT_HERSHEY_SIMPLEX, 2, (0, 0, 255), 4, cv2.LINE_AA)
        
        # 记录该帧处理完毕的时间
        end_time = time.time()
        # 计算每秒处理图像帧数FPS
        FPS = 1/(end_time - start_time)  
        # 图片,添加的文字,左上角坐标,字体,字体大小,颜色,线宽,线型
        img = cv2.putText(img, 'FPS  '+str(int(FPS)), (50, 80), cv2.FONT_HERSHEY_SIMPLEX, 2, (255, 0, 255), 4, cv2.LINE_AA)
    
        return img
    
  • 调用摄像头获取每帧(模板)

    # 调用摄像头逐帧实时处理模板
    # 不需修改任何代码,只需修改process_frame函数即可
    # 同济子豪兄 2021-7-8
    
    # 导入opencv-python
    import cv2
    import time
    
    # 获取摄像头,传入0表示获取系统默认摄像头
    cap = cv2.VideoCapture(1)
    
    # 打开cap
    cap.open(0)
    
    # 无限循环,直到break被触发
    while cap.isOpened():
        # 获取画面
        success, frame = cap.read()
        if not success:
            print('Error')
            break
        
        ## !!!处理帧函数
        frame = process_frame(frame)
        
        # 展示处理后的三通道图像
        cv2.imshow('my_window',frame)
    
        if cv2.waitKey(1) in [ord('q'),27]: # 按键盘上的q或esc退出(在英文输入法下)
            break
        
    # 关闭摄像头
    cap.release()
    
    # 关闭图像窗口
    cv2.destroyAllWindows()
    

【D2】推理引擎ONNX Runtime部署-预测摄像头实时画面-中文

也是要在本地运行的
和上面代码基本一样,除了
1.需要导入中文字体

# 下载中文字体文件
!wget https://zihao-openmmlab.obs.cn-east-3.myhuaweicloud.com/20220716-mmclassification/dataset/SimHei.ttf

# 导入中文字体,指定字号
font = ImageFont.truetype('SimHei.ttf', 32)

2.在处理单帧画面的函数部分
中文:

# 在图像上写字
    for i in range(len(confs)):
        pred_class = idx_to_labels[pred_ids[i]]
        text = '{:<15} {:>.3f}'.format(pred_class, confs[i])
        # 文字坐标,中文字符串,字体,rgba颜色
        draw.text((50, 100 + 50 * i), text, font=font, fill=(255, 0, 0, 1))
    img = np.array(img_pil) # PIL 转 array
    img = cv2.cvtColor(img, cv2.COLOR_RGB2BGR) # RGB转BGR

英文:

# 在图像上写英文
	    for i in range(len(confs)):
	        pred_class = idx_to_labels[pred_ids[i]]
	        text = '{:<15} {:>.3f}'.format(pred_class, confs[i])

【B】部署自己训练的水果图像分类模型

【A】安装配置环境

  • 安装基础工具包

    !pip install numpy pandas matplotlib tqdm opencv-python pillow onnx onnxruntime -i https://pypi.tuna.tsinghua.edu.cn/simple
    
  • 安装Pytorch

    !pip3 install torch torchvision --extra-index-url https://download.pytorch.org/whl/cu113
    
  • 创建目录

    import os
    
    # 存放测试图片
    os.mkdir('test_img')
    
    # 存放结果文件
    os.mkdir('output')
    
    # 存放训练得到的模型权重
    os.mkdir('checkpoints')
    
    # 下载测试图像文件 至 test_img 文件夹
    
    # 草莓图像,来源:https://www.pexels.com/zh-cn/photo/4828489/
    !wget https://zihao-openmmlab.obs.cn-east-3.myhuaweicloud.com/20220716-mmclassification/test/0818/test_草莓.jpg -P test_img
    
    !wget https://zihao-openmmlab.obs.myhuaweicloud.com/20220716-mmclassification/test/0818/test_fruits.jpg -P test_img
    
    !wget https://zihao-openmmlab.obs.myhuaweicloud.com/20220716-mmclassification/test/0818/test_orange_2.jpg -P test_img 
    
    !wget https://zihao-openmmlab.obs.myhuaweicloud.com/20220716-mmclassification/test/0818/test_bananan.jpg -P test_img
    
    !wget https://zihao-openmmlab.obs.myhuaweicloud.com/20220716-mmclassification/test/0818/test_kiwi.jpg -P test_img
    
    !wget https://zihao-openmmlab.obs.myhuaweicloud.com/20220716-mmclassification/test/0818/test_石榴.jpg -P test_img
    
    !wget https://zihao-openmmlab.obs.myhuaweicloud.com/20220716-mmclassification/test/0818/test_orange.jpg -P test_img
    
    !wget https://zihao-openmmlab.obs.myhuaweicloud.com/20220716-mmclassification/test/0818/test_lemon.jpg -P test_img
    
    !wget https://zihao-openmmlab.obs.myhuaweicloud.com/20220716-mmclassification/test/0818/test_火龙果.jpg -P test_img
    
    !wget https://zihao-openmmlab.obs.cn-east-3.myhuaweicloud.com/20220716-mmclassification/test/watermelon1.jpg -P test_img
    
    !wget https://zihao-openmmlab.obs.cn-east-3.myhuaweicloud.com/20220716-mmclassification/test/banana1.jpg -P test_img
    
  • 下载中文字体文件

    !wget https://zihao-openmmlab.obs.cn-east-3.myhuaweicloud.com/20220716-mmclassification/dataset/SimHei.ttf
    
  • 下载训练好的模型文件

    # 下载样例模型文件
    !wget https://zihao-openmmlab.obs.cn-east-3.myhuaweicloud.com/20220716-mmclassification/checkpoints/fruit30_pytorch_20220814.pth -P checkpoints
    

【B】导出ONNX模型

  • 导入工具包
  • 导入训练好的模型
  • Pytorch模型转ONNX模型
  • 验证onnx模型导出成功
  • 以可读的形式打印计算图
  • 使用Netron对onnx模型可视化

【C】推理引擎ONNX Runtime部署-预测单张图像

  • 导入工具包

  • 载入onnx模型,获取ONNX Runtime推理器

  • 构造输入,获取输出结果

  • 预处理

  • 载入测试图像

  • 运行预处理

  • ONNX Runtime预测

  • 解析预测结果(A中这部分是柱状图可视化)
    载入类别和对应ID

    idx_to_labels = np.load('idx_to_labels.npy', allow_pickle=True).item()
    
    print(idx_to_labels)
    

    {0: ‘哈密瓜’,
    , 1: ‘圣女果’,
    , 2: ‘山竹’,
    , 3: ‘杨梅’,
    , 4: ‘柚子’,
    , 5: ‘柠檬’,
    , 6: ‘桂圆’,
    , 7: ‘梨’,
    , 8: ‘椰子’,
    , 9: ‘榴莲’,
    , 10: ‘火龙果’,
    , 11: ‘猕猴桃’,
    , 12: ‘石榴’,
    , 13: ‘砂糖橘’,
    , 14: ‘胡萝卜’,
    , 15: ‘脐橙’,
    , 16: ‘芒果’,
    , 17: ‘苦瓜’,
    , 18: ‘苹果-红’,
    , 19: ‘苹果-青’,
    , 20: ‘草莓’,
    , 21: ‘荔枝’,
    , 22: ‘菠萝’,
    , 23: ‘葡萄-白’,
    , 24: ‘葡萄-红’,
    , 25: ‘西瓜’,
    , 26: ‘西红柿’,
    , 27: ‘车厘子’,
    , 28: ‘香蕉’,
    , 29: ‘黄瓜’}

设置matplotlib中文字体

# Linux操作系统,例如 云GPU平台:https://featurize.cn/?s=d7ce99f842414bfcaea5662a97581bd1
# 如果遇到 SSL 相关报错,重新运行本代码块即可
!wget https://zihao-openmmlab.obs.cn-east-3.myhuaweicloud.com/20220716-mmclassification/dataset/SimHei.ttf -O /environment/miniconda3/lib/python3.7/site-packages/matplotlib/mpl-data/fonts/ttf/SimHei.ttf
!rm -rf /home/featurize/.cache/matplotlib

import matplotlib
matplotlib.rc("font",family='SimHei') # 中文字体

import matplotlib.pyplot as plt
%matplotlib inline

plt.figure(figsize=(22, 10))

x = idx_to_labels.values()
y = pred_softmax.cpu().detach().numpy()[0] * 100
width = 0.45 # 柱状图宽度

ax = plt.bar(x, y, width)

plt.bar_label(ax, fmt='%.2f', fontsize=15) # 置信度数值
plt.tick_params(labelsize=20) # 设置坐标文字大小

plt.title(img_path, fontsize=30)
plt.xticks(rotation=45) # 横轴文字旋转
plt.xlabel('类别', fontsize=20)
plt.ylabel('置信度', fontsize=20)
plt.show()

在这里插入图片描述

【D】水果分类-预测摄像头实时画面-中文

  • 注意事项
    本代码需在连接摄像头的本地运行,不能在云GPU平台运行

  • 导入工具包

  • 导入中文字体

  • 载入onnx模型,获取ONNX Runtime推理器

  • 载入类别和ID对应字典

  • 图像预处理

  • 预测摄像头单帧画面
    – 调用摄像头获取一帧画面
    画面转成RGB的Pillow格式

    img_bgr.shape #(720, 1280, 3)
    
    img_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB) # BGR转RGB
    
    img_pil = Image.fromarray(img_rgb)
    
    print(img_pil)
    
  • 预处理

  • ONNX Runtime预测

  • 解析top-n预测结果的类别和置信度

  • 在图像上写中文

  • 处理单帧画面的函数(中文)

  • 调用摄像头获取每帧(模板)

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值