1 准备工作
在 将pytorch生成的onnx模型转换成.bin模型 文章中,已经得到了.bin
模型,接下来就是到地平线的开发板上运行啦。需要准备的东西如下:主要是想要预测的图片、.bin
文件,一个.py
用于运行。
我们的目标是使用python进行推理,故不涉及c++的内容。
2 test_mobilenetv2.py详解
直接看代码中的注释即可。
from hobot_dnn import pyeasy_dnn # 用于加载.bin模型
import numpy as np
import cv2
import json
from PIL import Image
import matplotlib.pyplot as plt # 用于可视化
# ------------------------------------------#
# 针对二维numpy矩阵每一行进行softmax操作
# ------------------------------------------#
def softmax_2D(X):
"""
X: np.array. Probably should be floats.
return: 二维矩阵
"""
# looping through rows of X
# 循环遍历X的行
ps = np.empty(X.shape)
for i in range(X.shape[0]):
ps[i,:] = np.exp(X[i,:])
ps[i,:] /= np.sum(ps[i,:])
return ps
# ------------------------------------------#
# 获取模型输入或输出的一些属性信息
# ------------------------------------------#
def print_properties(pro):
print("tensor type:", pro.tensor_type) # 对于输入: RGB、BGR那些,对于输出:float32这种
print("data type:", pro.dtype) # uint8、float332那些
print("layout:", pro.layout) # NCHW、NHWC
print("shape:", pro.shape) # (1, 224, 224, 3),对应于layout
# ------------------------------------------#
# 获取模型输入高和宽,用于图片resize
# ------------------------------------------#
def get_hw(pro):
if pro.layout == "NCHW":
return pro.shape[2], pro.shape[3]
else:
return pro.shape[1], pro.shape[2]
if __name__ == '__main__':
# 加载.bin模型
models = pyeasy_dnn.load('mobilenetv2_224x224_rgb.bin')
## -------------------------------------------------------#
## 第一次加载bin模型,根本不知道它的输入输出应该是什么,
## 以及怎么表示它的输入和输出,必须搞懂,懂了之后可删掉!
## -------------------------------------------------------#
# -----------------------------------------------------------------------------------#
# 模型输入的属性与写法,调用print_properties函数,内部解析见上面
# 为什么是models[0]?为什么是inputs[0]?
# 回答:内部代码应该是对应多个模型并行,封装成一个列表,对应模型输入也是不同的,成一个列表
# 我们只有一个模型,一个输入,故......
# -----------------------------------------------------------------------------------#
print("=" * 10, "inputs[0] properties", "=" * 10)
print_properties(models[0].inputs[0].properties)
print("inputs[0] name is:", models[0].inputs[0].name) # 顺便看个name属性
# -----------------------------------#
# 模型输出的属性与写法
# -----------------------------------#
print("=" * 10, "outputs[0] properties", "=" * 10)
print_properties(models[0].outputs[0].properties)
print("outputs[0] name is:", models[0].outputs[0].name)
# ------------------------------------------#
# 获取模型输入高和宽,用于图片resize
# 通常大家都是两者相等,看你自己网络怎么训练的,
# 也可以自己直接给值,不行的话,会报错的
# ------------------------------------------#
h, w = get_hw(models[0].inputs[0].properties)
img_path = './data/rose.jpg'
## ----------------------------------#
## 下面两行,用它显示图片而已
## ----------------------------------#
img = Image.open(img_path)
plt.imshow(img)
img = cv2.imread(img_path)
# resize的目标尺寸
des_dim = (w, h)
img = cv2.resize(np.array(img), des_dim, interpolation=cv2.INTER_AREA)
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
## -------------------------------------------------------#
## 此处是不需要进行transpose的,因为无论你的输入时NCHW还是NHWC,
## 板端都会自动给你转换成NHWC,留在这,只是为了写这段理解
## -------------------------------------------------------#
# img = img.transpose(2, 0, 1)
# 添加batch维度
img = np.expand_dims(img, 0)
# --------------------------------#
# class_indict用于可视化类别
# --------------------------------#
json_path = './class_indices.json'
with open(json_path, "r") as f:
class_indict = json.load(f)
## --------------------------------------------------------------#
## 模型前向推理得到输出,此时模型的输出格式(NCHW/NHWC)默认与原网络相同
## pytorch模型默认输出为NCHW
## 疑问:outputs里面是什么?往下看
## --------------------------------------------------------------#
outputs = models[0].forward(img)
# --------------------------------------------------------------------#
# NCWH输出模型如何进行分类后处理?
# !以每次预测一张图片为例!
# outputs[0]里面又是什么?outputs[0].buffer又是什么?
# 不看概率的话,单张图,模型输出NHWC也可以这样处理
# --------------------------------------------------------------------#
print("=" * 10, "Classification result", "=" * 10)
# --------------------------------------------------------------------#
# np.argmax(): 没指定axis参数,会把数组拍扁成一行,取其中最大值的索引
# cls_id是一个int数字,代表它对应的类别
# --------------------------------------------------------------------#
cls_id = np.argmax(outputs[0].buffer)
# print("cls id:", cls_id) # 0
cls_name = "class_name: {}".format(class_indict[str(cls_id)])
print(cls_name) # class_name: roses
### --------------------------工程已经结束,下面是探索--------------------------#
"""
print('outputs:', outputs) # 元组里放张量位置
print('outputs[0]:', outputs[0]) # 张量位置
print('outputs[0].buffer:', outputs[0].buffer) # 张量内容
print('outputs[0].buffer type:', type(outputs[0].buffer)) # <class 'numpy.ndarray'>
"""
# -----------------------------------#
# 想知道图片属于某类别的概率?
# 能不能直接网络输出是NHWC呢?
# 回答:可以,下一篇文章介绍。
# -----------------------------------#
outputs_NHWC = outputs[0].buffer.transpose(0,3,2,1)
# print(outputs_NHWC[0][0]) # [[ 3.1698108 -1.1617191 -1.5916455 -0.43572566 -0.38660535]]
# -----------------------------------#
# 二维矩阵,经过softmax转化为概率
# softmax_2D按行转化,一行一个样本
# -----------------------------------#
predict_probability = softmax_2D(outputs_NHWC[0][0])
print('predict_probability:', predict_probability) # array([[0.1],[0.2],[0.3],[0.3],[0.1]])
# ----------------------------------------------#
# argmax得到最大概率索引,也就是类别对应索引
# ----------------------------------------------#
predict_cla = np.argmax(predict_probability, axis=-1)
# print('predict_cla:',predict_cla) # array([2])
# ----------------------------------------#
# 保存图片预测结果并可视化
# ----------------------------------------#
print_res = "class: {} prob: {:.3}".format(class_indict[str(predict_cla[0])],
predict_probability[0][predict_cla[0]])
# print(print_res) # class: roses prob: 0.928
plt.title(print_res)
plt.savefig("./result.jpg")
plt.show() # 可惜板端接了屏幕还不行,后面再研究
# ----------------------------------------#
# 看看图片属于每个类别的可能性
# ----------------------------------------#
print("="*10, "every class probability", "="*10)
for i in range(len(predict_probability)):
for j in range(len(predict_probability[0])):
print("class: {:10} prob: {:.3}".format(class_indict[str(j)],
predict_probability[i][j]))
3 运行演示
将上方的文件传到x3开发板上,直接使用命令:
python3 mobilenetv2.py
终端界面如下:
保存的result.jpg
如下:
至此,完成上板工作。