本文为稀土技术社区首发签约文章,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下面的目录结构是这样的:
咱们这儿或许有点疑惑,从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)
咱们来看看咱们的小猫咪长什么样:
图画预处理
上面提到咱们的输入需求是(1, 3, 520, 520)大小的,所以咱们要进行resize操作:
img_bgr_resize = cv2.resize(img_bgr, (520, 520)) # 缩放尺度
enmmm,再来看看resize后的图画吧:【这是用plt库显现的成果】
接着还需求进行其它的一下预处理操作,如下:
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
的维度:
还记得咱们要对这个维度怎么操作吗,如下:
咱们要获取每个chanel中的最大值:
pred_mask = ort_output.argmax(1)[0] #获取ort_output数组中的第一个二维元素
此时pred_mask
维度为:
咱们能够通过np.unique(pred_mask)
来看看pred_mask
中有哪些值:
从上图能够看出有0和8,0表明布景,8表明cat。这个和voc类别文档中的是一致的,文档中布景没有写。
接着咱们能够简略的完成一个可视化,先定义一个字典:
# [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
,如下:
成果仍是非常不错的。
根据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__)
我的版别是这样的:
这些准备好了之后,咱们就能够运用mmsegmentation进行语义切割使命了,操作也很简略,首要便是对各种配置文件的修正。因为本节首要介绍模型布置,这儿如何进行练习就不叙说了。不清楚的能够点击☞☞☞了解概况。
这儿我直接拿同济子豪兄得到的西瓜语义切割ONNX模型来举例了,呜呜,懒的自己从头练习了。下载连接如下:
链接: pan.baidu.com/s/1E7Q0P79n… 提取码: luq8
下载完成后解压就得到了ONNX格局的模型。
这儿先来简略介绍一下西瓜语义切割数据集:
一共有6个类别,分别为:
- 0:布景
- 1:红壤
- 2:绿壳
- 3:白皮
- 4:黒籽
- 5:白籽
下面是标示的图画:
咱们有了ONNX模型后,咱们就能够运用ONNX Runtime推理器进行推理了。
# ONNX 模型途径
onnx_path = 'mmseg2onnx_fastscnn/end2end.onnx'
ort_session = onnxruntime.InferenceSession(onnx_path, providers=['CUDAExecutionProvider', 'CPUExecutionProvider'])
其实到这儿就和前面根据FCN进行语义切割差不多了,我讲这部分便是想让咱们了解了解MMCV这个东西,但是这节也没有详细介绍啦,后期或许会出一些根据MMCV完成目标检测、语义切割的博客。
西瓜数据集一个有6个类,它们的值都很挨近,所以色彩很相似,咱们将这6个值映射到不同的色彩,这样可视化出来愈加漂亮:
# 各类别的配色计划(BGR)
palette = [ ['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]
这个代码不知道咱们能否了解,有不了解的其实调试一下就会恍然大悟:
看了上图的调试过程,咱们是不是就明晰了呢,最后palette_dict
是一个字典,内容如下:
接下来,咱们封装一个处理单张图画的函数,如下:【其实这部分便是把上节的几个小部分整合到了一起,没什么难度】
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的成果如下:
enmmmm,我没有西瓜啊,所以这个是手机里边的图片
接着运用img_bgr = process_frame(frame)
来推理这张图片,切割成果如下:
能够看到,FPS为1.71,针对我的电脑它现已很快了,咱们能够试试不运用ONNX Runtime推理的FPS,你会发现极端极端低。
小结
本篇就为咱们介绍到这儿了喔,期望咱们都有所收成。⛳⛳⛳吐槽一下,今日踢球又一次伤到脚踝了,坏了,又得在宿舍呆好多天,这篇后边感觉自己写的有点仓促了,咱们多担待,不说了,上床躺尸。
如若文章对你有所协助,那就