本文为稀土技术社区首发签约文章,30天内禁止转载,30天后未获授权禁止转载,侵权必究!

作者简介:秃头小苏,致力于用最通俗的言语描述问题

专栏引荐:深度学习网络原理与实战

近期目标:写好专栏的每一篇文章

支撑小苏:点赞、保藏⭐、留言

写在前面

在前面的几节中,我现已为咱们介绍了深度学习在分类模型中的布置,分为以下三节,不清楚的能够去看一看:

  • 深度学习模型布置篇——从0布置深度学习分类模型(一)
  • 深度学习模型布置篇——从0布置深度学习分类模型(二)
  • 深度学习模型布置篇——利用Flask完成深度学习模型布置(三)

那么这篇我将和咱们唠唠如何布置语义切割模型,咱们千万不要潜意识里觉得语义切割模型很难,其实它是很简略的,不清楚的能够去看看语义切割的开山之作——FCN,我也做过原理详解和源码实战篇,不清楚的能够去看看,相信你定会有所收成:

  • 深度学习语义切割篇——FCN原理详解篇
  • 深度学习语义切割篇——FCN源码解析篇

知道了语义切割的根本知识,你就能够来看这篇博客啦。当然了,我还做过语义切割的其它系列,感兴趣的欢迎去踩我的主页。

准备好了喵,咱们这就发车~~~


这儿我想给咱们说一下本文的行文安排,首先我会根据最基础的语义切割模型FCN,给咱们介绍语义切割模型布置的流程,熟悉这个流程之后为咱们介绍介绍mmcv库,根据这个库来完成语义切割模型布置。


根据FCN布置语义切割模型

好啦,咱们这就开端了喔,本末节运用的是FCN模型进行模型布置,因此主张你先读读我写在前面中给出的两篇文章,对FCN完成语义切割有一个根本的了解。

导入东西包

import onnxruntime
import numpy as np
import cv2
from src import fcn_resnet50
import torch
​
import matplotlib.pyplot as plt
%matplotlib inline

咱们留意一下这儿from src import fcn_resnet50,咱们从src下的fcn_model.py文件中导入了fcn模型结构,src下面的目录结构是这样的:

深度学习模型部署篇——从0部署深度学习语义分割模型(四)

咱们这儿或许有点疑惑,从fcn_model.py中导入文件,为什么这儿只写了from src呢,其实其它的内容写在了__init__.py文件中,文件内容如下:

对于此不熟悉的能够看一下我的这篇文章。

创建模型

aux = False
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
classes = 20
# create model
model = fcn_resnet50(aux=aux, num_classes=classes+1)
​
weights_path = "./model_29.pth"
# delete weights about aux_classifier
weights_dict = torch.load(weights_path, map_location='cpu')['model']
for k in list(weights_dict.keys()):
  if "aux" in k:
    del weights_dict[k]

这部分其实便是FCN猜测部分的代码,我直接复制过来了。

加载模型并设置为推理形式

# 加载模型
model.load_state_dict(weights_dict)
# 设置为推理形式
model = model.eval().to(device)

pytorch转ONNX格局

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

这部分和图画分类是一致的,需求留意的是这儿的输入需求是(1, 3, 520, 520)的大小。

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

# ONNX 模型途径
onnx_path = './model_29.onnx'
ort_session = onnxruntime.InferenceSession(onnx_path, providers=['CUDAExecutionProvider', 'CPUExecutionProvider'])

providers=['CUDAExecutionProvider', 'CPUExecutionProvider']参数表明自己根据硬件挑选GPU或者CPU。

载入推理图画

img_path = 'cat.jpg'
img_bgr = cv2.imread(img_path)

咱们来看看咱们的小猫咪长什么样:

深度学习模型部署篇——从0部署深度学习语义分割模型(四)

图画预处理

上面提到咱们的输入需求是(1, 3, 520, 520)大小的,所以咱们要进行resize操作:

img_bgr_resize = cv2.resize(img_bgr, (520, 520)) # 缩放尺度

enmmm,再来看看resize后的图画吧:【这是用plt库显现的成果】

深度学习模型部署篇——从0部署深度学习语义分割模型(四)

接着还需求进行其它的一下预处理操作,如下:

img_tensor = img_bgr_resize
​
# BGR 三通道的均值
mean = (123.675, 116.28, 103.53)
​
# BGR 三通道的标准差
std = (58.395, 57.12, 57.375)
​
# 归一化
img_tensor = (img_tensor - mean) / std
img_tensor = img_tensor.astype('float32')
​
# BGR 转 RGB
img_tensor = cv2.cvtColor(img_tensor, cv2.COLOR_BGR2RGB)
​
# 调整维度
img_tensor = np.transpose(img_tensor, (2, 0, 1))  #h w c --> c h w
# 扩大 batch-size 维度
input_tensor = np.expand_dims(img_tensor, axis=0)

ONNX Runtime猜测

# ONNX Runtime 输入
ort_inputs = {'input': input_tensor}
# onnx runtime 输出
ort_output = ort_session.run(['output'], ort_inputs)[0]

咱们能够来看看ort_output的维度:

深度学习模型部署篇——从0部署深度学习语义分割模型(四)

还记得咱们要对这个维度怎么操作吗,如下:

深度学习模型部署篇——从0部署深度学习语义分割模型(四)

咱们要获取每个chanel中的最大值:

pred_mask = ort_output.argmax(1)[0] #获取ort_output数组中的第一个二维元素

此时pred_mask维度为:

深度学习模型部署篇——从0部署深度学习语义分割模型(四)

咱们能够通过np.unique(pred_mask)来看看pred_mask中有哪些值:

深度学习模型部署篇——从0部署深度学习语义分割模型(四)

从上图能够看出有0和8,0表明布景,8表明cat。这个和voc类别文档中的是一致的,文档中布景没有写。

深度学习模型部署篇——从0部署深度学习语义分割模型(四)

接着咱们能够简略的完成一个可视化,先定义一个字典:

# [127, 127, 127]表明灰色 ;[0, 180, 180]表明黄色
palette_dict = {0: [127, 127, 127], 8: [0, 180, 180]} 

然后将0和8两个类别映射成不同的色彩:

opacity = 0.2 # 透明度,越大越挨近原图
# 将猜测的整数ID,映射为对应类别的色彩
pred_mask_bgr = np.zeros((pred_mask.shape[0], pred_mask.shape[1], 3))
for idx in palette_dict.keys():
  pred_mask_bgr[np.where(pred_mask==idx)] = palette_dict[idx]
pred_mask_bgr = pred_mask_bgr.astype('uint8')
​
# 将语义切割猜测图和原图叠加显现
pred_viz = cv2.addWeighted(img_bgr_resize, opacity, pred_mask_bgr, 1-opacity, 0)

咱们能够来看下最终的猜测成果,即pred_viz,如下:

深度学习模型部署篇——从0部署深度学习语义分割模型(四)

成果仍是非常不错的。

根据MMCV库完成语义切割模型布置

首先来介绍一下MMCV库,其是一个用于计算机视觉和多媒体计算的开源东西包,首要用于深度学习项目的开发和研究。MMCV是由中国科学院自动化研究所(Institute of Automation, Chinese Academy of Sciences)开发和保护的,它供给了许多用于图画和视频处理、计算机视觉使命、模型练习和布置的实用东西和组件。运用这个库咱们能够很方便的完成各种计算机视觉使命,首先咱们先来装置一下这个库:

pip install -U openmim
mim install mmengine
mim install mmcv==2.0.0

接着装置其它的东西包:

pip install opencv-python pillow matplotlib seaborn tqdm pytorch-lightning 'mmdet>=3.1.0' -i https://pypi.tuna.tsinghua.edu.cn/simple

这儿运用了清华园镜像进行装置,会快许多。咱们留意装置包的时分不要翻开体系代理,不然会装置失败。因为咱们要进行的是语义切割使命,因此需求下载mmsegmentation 源代码:

git clone https://github.com/open-mmlab/mmsegmentation.git -b v1.1.1

然后咱们进入mmsegmentation 目录装置MMSegmentation库:

# 进入主目录
import os
os.chdir('mmsegmentation')
# 装置`MMSegmentation`库
pip install -v -e .

装置好后,咱们来验证下咱们是否装置成功:

# 查看 mmcv
import mmcv
from mmcv.ops import get_compiling_cuda_version, get_compiler_version
print('MMCV版别', mmcv.__version__)
print('CUDA版别', get_compiling_cuda_version())
print('编译器版别', get_compiler_version())
# 查看 mmsegmentation
import mmseg
from mmseg.utils import register_all_modules
from mmseg.apis import inference_model, init_model
print('mmsegmentation版别', mmseg.__version__)

我的版别是这样的:

深度学习模型部署篇——从0部署深度学习语义分割模型(四)

深度学习模型部署篇——从0部署深度学习语义分割模型(四)

这些准备好了之后,咱们就能够运用mmsegmentation进行语义切割使命了,操作也很简略,首要便是对各种配置文件的修正。因为本节首要介绍模型布置,这儿如何进行练习就不叙说了。不清楚的能够点击☞☞☞了解概况。

这儿我直接拿同济子豪兄得到的西瓜语义切割ONNX模型来举例了,呜呜,懒的自己从头练习了。下载连接如下:

链接: pan.baidu.com/s/1E7Q0P79n… 提取码: luq8

深度学习模型部署篇——从0部署深度学习语义分割模型(四)

下载完成后解压就得到了ONNX格局的模型。


这儿先来简略介绍一下西瓜语义切割数据集:

深度学习模型部署篇——从0部署深度学习语义分割模型(四)

一共有6个类别,分别为:

  • 0:布景
  • 1:红壤
  • 2:绿壳
  • 3:白皮
  • 4:黒籽
  • 5:白籽

下面是标示的图画:

深度学习模型部署篇——从0部署深度学习语义分割模型(四)


咱们有了ONNX模型后,咱们就能够运用ONNX Runtime推理器进行推理了。

# ONNX 模型途径
onnx_path = 'mmseg2onnx_fastscnn/end2end.onnx'
ort_session = onnxruntime.InferenceSession(onnx_path, providers=['CUDAExecutionProvider', 'CPUExecutionProvider'])

其实到这儿就和前面根据FCN进行语义切割差不多了,我讲这部分便是想让咱们了解了解MMCV这个东西,但是这节也没有详细介绍啦,后期或许会出一些根据MMCV完成目标检测、语义切割的博客。

西瓜数据集一个有6个类,它们的值都很挨近,所以色彩很相似,咱们将这6个值映射到不同的色彩,这样可视化出来愈加漂亮:

# 各类别的配色计划(BGRpalette = [   ['background', [127,127,127]],
   ['red', [0,0,200]],
   ['green', [0,200,0]],
   ['white', [144,238,144]],
   ['seed-black', [30,30,30]],
   ['seed-white', [180,180,180]]
]
​
palette_dict = {}
for idx, each in enumerate(palette):
  palette_dict[idx] = each[1]

这个代码不知道咱们能否了解,有不了解的其实调试一下就会恍然大悟:

深度学习模型部署篇——从0部署深度学习语义分割模型(四)

看了上图的调试过程,咱们是不是就明晰了呢,最后palette_dict是一个字典,内容如下:

深度学习模型部署篇——从0部署深度学习语义分割模型(四)

接下来,咱们封装一个处理单张图画的函数,如下:【其实这部分便是把上节的几个小部分整合到了一起,没什么难度】

opacity = 0.2 # 透明度,越大越挨近原图
def process_frame(img_bgr):
  
  '''
   输入摄像头画面 bgr-array,输出图画 bgr-array
   '''
  
  # 记录该帧开端处理的时刻
  start_time = time.time()
  
  # 从原图中裁剪出高宽比1:2的最大图画
  h, w = img_bgr.shape[0], img_bgr.shape[1]
  new_h = w // 2 # 横屏图片,截取一半的宽度,作为新的高度
  img_bgr_crop = img_bgr[0:new_h, :]
  
  # 缩放至模型要求的高1024 x 宽2048像素
  img_bgr_resize = cv2.resize(img_bgr_crop, (2048, 1024)) # 缩放尺度
  
  # 预处理
  img_tensor = img_bgr_resize
  mean = (123.675, 116.28, 103.53) # BGR 三通道的均值
  std = (58.395, 57.12, 57.375) # BGR 三通道的标准差# 归一化
  img_tensor = (img_tensor - mean) / std
  img_tensor = img_tensor.astype('float32')
  img_tensor = cv2.cvtColor(img_tensor, cv2.COLOR_BGR2RGB) # BGR 转 RGB
  img_tensor = np.transpose(img_tensor, (2, 0, 1)) # 调整维度
  input_tensor = np.expand_dims(img_tensor, axis=0) # 扩大 batch-size 维度# ONNX Runtime猜测
  # ONNX Runtime 输入
  ort_inputs = {'input': input_tensor}
  # onnx runtime 输出
  ort_output = ort_session.run(['output'], ort_inputs)[0]
  pred_mask = ort_output[0][0]
​
  # 将猜测的整数ID,映射为对应类别的色彩
  pred_mask_bgr = np.zeros((pred_mask.shape[0], pred_mask.shape[1], 3))
  for idx in palette_dict.keys():
    pred_mask_bgr[np.where(pred_mask==idx)] = palette_dict[idx]
  pred_mask_bgr = pred_mask_bgr.astype('uint8')
​
  # 将语义切割猜测图和原图叠加显现
  pred_viz = cv2.addWeighted(img_bgr_resize, opacity, pred_mask_bgr, 1-opacity, 0)
  
  img_bgr = pred_viz
  
  # 记录该帧处理完毕的时刻
  end_time = time.time()
  # 计算每秒处理图画帧数FPS
  FPS = 1/(end_time - start_time)
​
  # 在画面上写字:图片,字符串,左上角坐标,字体,字体大小,色彩,字体粗细
  scaler = 2 # 文字大小
  FPS_string = 'FPS {:.2f}'.format(FPS) # 写在画面上的字符串
  img_bgr = cv2.putText(img_bgr, FPS_string, (25 * scaler, 100 * scaler), cv2.FONT_HERSHEY_SIMPLEX, 1.25 * scaler, (255, 0, 255), 2 * scaler)
  
  return img_bgr

接着咱们能够来调用摄像头一帧图画来看看成果:

# 获取摄像头,0为电脑默许摄像头,1为外接摄像头
cap = cv2.VideoCapture(0)
​
# 摄影
time.sleep(1) # 运转本代码后等几秒摄影
# 从摄像头捕获一帧画面
success, frame = cap.read()
​
cap.release() # 关闭摄像头
cv2.destroyAllWindows() # 关闭图画窗口

frame的成果如下:

深度学习模型部署篇——从0部署深度学习语义分割模型(四)

enmmmm,我没有西瓜啊,所以这个是手机里边的图片

接着运用img_bgr = process_frame(frame)来推理这张图片,切割成果如下:

深度学习模型部署篇——从0部署深度学习语义分割模型(四)

能够看到,FPS为1.71,针对我的电脑它现已很快了,咱们能够试试不运用ONNX Runtime推理的FPS,你会发现极端极端低。

小结

本篇就为咱们介绍到这儿了喔,期望咱们都有所收成。⛳⛳⛳吐槽一下,今日踢球又一次伤到脚踝了,坏了,又得在宿舍呆好多天,这篇后边感觉自己写的有点仓促了,咱们多担待,不说了,上床躺尸。

如若文章对你有所协助,那就

        

深度学习模型部署篇——从0部署深度学习语义分割模型(四)