从GitHub上找了个Yolov3的TensorFlow实现1,自己标注数据集,训练出结果后,跑起来实在是太慢了,800+ms一张图片,毫无实用性。最终应用场景又不可能整个工作站、2080ti什么的,头疼。无意间发现了OpenVINO和神经计算棒二代,听说能加速?那就试试咯
无非就三步嘛
1、安装OpenVINO,配置好环境
2、把TensorFlow模型转换成xml+bin文件
3、调用api,输入数据得出结果
不得不说官方文档2的确是最好的教程,比网上绝大多数帖子博客(包括我的哈哈哈)都要详细,建议先看。英文可以翻译,词汇不算复杂。也有中文文档,安装部分写得不错
这是win10安装教程,概括一下就是要安装以下软件:
其中VS及生成工具安装可能不大好找,这里贴出微软官方下载链接,没需求的话安装2015就行了。
建议先安装好其他三项再安装OpenVINO,会出现这样的界面,不然会提醒你还有什么没安装。
建议默认安装路径,省事。2020版本貌似不用自定义选择组件安装了。
这是树莓派安装教程,相较于win10来说,它不转换模型,只有推理引擎。需要安装以下软件:
OpenVINO的安装包只有20来M,安装起来非常快,还包含了OpenCV,不用编译!
win10平台是一个bat文件,打开cmd运行即可,然后在这个命令行窗口进行转换推理等操作
树莓派里是一个sh文件,操作同上。可以执行以下命令,意思是在.bashrc里加入source /opt/intel/openvino/bin/setupvars.sh
这么一句,每次打开终端都会运行设置环境变量的指令。
echo "source /opt/intel/openvino/bin/setupvars.sh" >> ~/.bashrc
win10平台不用管,Linux和树莓派上需要设置USB规则,具体看官方教程,敲几句命令的事。
设置完环境变量后,win10里是执行一个bat,它会下载xml+bin文件,这就是OpenVINO的模型文件,然后调用Build Tools和CMake生成一个exe文件,再去执行。树莓派类似。
说明:如果安装在了其他路径下,可能要去demo bat或者sh文件里修改对应的路径。
win10:
树莓派:
验证不是必须的,想要直接使用可以跳过,但不建议
如上一番折腾后,我们的硬件平台就有了OpenVINO环境,可以继续干活了。
由于OpenVINO使用的是xml+bin文件,所以需要对其他框架的模型文件进行转换。
这是官方文档,安装所用框架对应的文件即可,不用全部下载。win10 TensorFlow如下:
cd "C:\Program Files (x86)\IntelSWTools\openvino_2020.1.033\deployment_tools\model_optimizer\install_prerequisites"
install_prerequisites_tf.bat
双引号是因为Program Files (x86)间有空格和特殊字符。openvino_2020.1.033是我的版本,根据实际情况修改。
这是官方文档,先把checkpoint文件转为pb文件,官方文档往上划一下就有,Freezing Custom Models in Python*。把pb文件转换为xml+bin如下:
cd "C:\Program Files (x86)\IntelSWTools\openvino_2020.1.033\deployment_tools\model_optimizer"
python mo_tf.py --input_model yolov3_coco.pb --input_shape [1,416,416,3]
说明:python还是python3看你的配置;–input_model后面跟要转换的pb文件的路径;–input_shape跟输入参数的形状,我的加了这个才转换成功。这是转化成fp32精度的,如果要给树莓派用,需要转化成fp16精度的,加上--data_type FP16
。
下载好转换所需文件,再把pb文件或者其他模型文件转换成xml+bin,这样我们就有了推理的基础。到这里,即便没有TensorFlow等深度学习库也是完全可以的,不依赖它们了,只要有OpenVINO环境和xml+bin文件就能实现CNN推理。
终于到了这一步,调用api嘛,因为不会C++,所以说说Python的方法
#! /usr/bin/env python
# coding=utf-8
import cv2
import numpy as np
import utils3 as utils
from openvino.inference_engine import IENetwork, IEPlugin # 导入openvino库
num_classes = 80
input_size = 416
# 搭建网络,调用IENetwork,返回net对象,参数为xml和bin文件的路径
model_xml_CPU = r'yolov3_coco.xml'
model_bin_CPU = r'yolov3_coco.bin'
net = IENetwork(model=model_xml_CPU, weights=model_bin_CPU)
# 定义输入输出
input_blob = next(iter(net.inputs)) # 迭代器
out_blob = next(iter(net.outputs))
print(input_blob, out_blob)
n, c, h, w = net.inputs[input_blob].shape
print(n,c,h,w)
# 加载设备,调用IEPlugin,返回设备对象,参数为设备名,如CPU、GPU、MYRIAD
plugin = IEPlugin(device='CPU')
# 加载网络,调用设备对象的load方法,返回执行器,参数为网络
exec_net = plugin.load(network=net)
print('load ok!')
# 获取图像
img_path = r'market.jpg'
original_image = cv2.imread(img_path)
# 图像处理,从(416, 416, 3)到(1, 3, 416, 416)
original_image_size = original_image.shape[:2]
image_data = utils.image_preporcess(np.copy(original_image), [input_size, input_size])
image_data = image_data[np.newaxis, ...]
# from (1, 416, 416, 3) to (1, 416, 416, 3)
op_img_data = np.rollaxis(image_data, 3, 1)
# 推理模型,调用执行器的infer方法,返回模型输出,输入为格式化图像
outputs = exec_net.infer(inputs={input_blob: op_img_data})
# from (1, 6, 52, 52, 3) to (1, 52, 52, 3, 6)
pred_sbbox = np.rollaxis(outputs['pred_sbbox/concat_2'], 1, 5)
pred_mbbox = np.rollaxis(outputs['pred_mbbox/concat_2'], 1, 5)
pred_lbbox = np.rollaxis(outputs['pred_lbbox/concat_2'], 1, 5)
pred_bbox = np.concatenate([np.reshape(pred_sbbox, (-1, 5 + num_classes)),
np.reshape(pred_mbbox, (-1, 5 + num_classes)),
np.reshape(pred_lbbox, (-1, 5 + num_classes))], axis=0)
bboxes = utils.postprocess_boxes(pred_bbox, original_image_size, input_size, 0.3)
bboxes = utils.nms(bboxes, 0.45, method='nms')
image = utils.draw_bbox(original_image, bboxes)
cv2.imshow('Result', image)
cv2.waitKey(0)
cv2.destroyAllWindows()
这是使用CPU进行推理,如果要用神经棒二代,需要加载fp16精度的模型,并且把device换成MYRIAD。
总结起来就4步,用IENetwork搭建网络,用IEPlugin加载设备,用设备的load方法加载网络,用执行器的infer方法进行推理。2019R3版本还有一个添加CPU扩展的步骤,2020版貌似合并了,不用添加CPU扩展了,撒花~
utils3.py3里放着一些常用函数,会在文末附上链接。
模型输出没搞懂,放一个例程,可以看出也是网络模型,设备,输入,推理的路子。
import cv2 as cv
# Load the model
net = cv.dnn.readNet('face-detection.xml', 'face-detection.bin')
# Specify target device
net.setPreferableTarget(cv.dnn.DNN_TARGET_CPU)
# Read an image
frame = cv.imread('face.jpg')
# Prepare input blob and perform an inference
blob = cv.dnn.blobFromImage(frame, size=(416, 416), ddepth=cv.CV_8U)
net.setInput(blob)
out = net.forward()
# Draw detected faces on the frame
for detection in out.reshape(-1, 7):
confidence = float(detection[2])
xmin = int(detection[3] * frame.shape[1])
ymin = int(detection[4] * frame.shape[0])
xmax = int(detection[5] * frame.shape[1])
ymax = int(detection[6] * frame.shape[0])
if confidence > 0.5:
cv.rectangle(frame, (xmin, ymin), (xmax, ymax), color=(0, 255, 0))
# Save the frame to an image file
cv.imwrite('out.png', frame)
每次启动都要打开cmd,设置环境变量,python xxx.py,巨麻烦,所以写个bat,一键启动!~
cd /d "C:\Program Files (x86)\IntelSWTools\openvino\bin"
call setupvars.bat
cd /d F:\TF_YOLOv3\Blog
python blog_openvino.py
pause
cd 要加上/d,不然不报错,当然也成功不了。pause是为了出错时能看到打印的错误信息,不调试可以去掉。
快要写吐了。。。。continue,也是踩了不少的坑啊
pi@raspberrypi:~/Desktop $ python3 yolov3_coco.py
Traceback (most recent call last):
File "yolov3_coco.py", line 11, in <module>
out = net.forward()
cv2.error: OpenCV(4.1.2-openvino) /home/jenkins/workspace/OpenCV/OpenVINO/build/opencv/modules/dnn/src/op_inf_engine.cpp:704:
error: (-215:Assertion failed) Failed to initialize Inference Engine backend:
AssertionFailed: newDims[newPerm[i]] == 1 in function 'initPlugin'
newDims[newPerm[i]] == 1,在官方论坛看到同样的报错,说是5D模型,这……??? 然后,把2019R3换成了2020,就解决了,不报错了。。。可能是转换的时候有问题吧,还没弄明白。
贴出的OpenVINO代码里,# from (1, 416, 416, 3) to (1, 416, 416, 3)
就是对输入维度的重组,可以在官方文档里找到说明——
–input_shape INPUT_SHAPE
… For example, [N,C,H,W] is used for Caffe* models and [N,H,W,C] for TensorFlow* models. Model Optimizer performs necessary transformations to convert the shape to the layout required by Inference Engine (N,C,H,W). …
最坑爹的是输出维度的重组,如下图
前面又是安装又是转换的折腾了那么久,你就给我看这个??差点崩溃,开始反思,难道是模型转换的锅?还是推理又出错了?不然为什么置信度能有上万的数?这也太迷了吧。
把outputs和TensorFlow跑的结果打印出来,好像,有几个数前五位对得上?
[[[[[3.70791960e+00 3.62859917e+00 7.69530010e+00 1.76488304e+01
1.38096511e-02 5.07005513e-01]
[4.27160788e+00 4.52064657e+00 2.25704193e+01 4.32643967e+01
1.55485570e-02 6.79710686e-01]
[4.34192467e+00 3.24738264e+00 4.82099113e+01 4.42287598e+01
1.73972249e-02 9.03145492e-01]]
[[[[[3.70792055e+00, 4.27160835e+00, 4.34192514e+00],
[1.17772388e+01, 1.23407335e+01, 1.20422506e+01],
[2.00990105e+01, 2.03807487e+01, 2.03778057e+01],
…,
[3.96122070e+02, 3.96281281e+02, 3.95539856e+02],
[4.03839752e+02, 4.04300812e+02, 4.03584259e+02],
[4.11999878e+02, 4.11900787e+02, 4.11700836e+02]],
又随便试了几个数字,都能找到!也就是说,又是维度乱了。。。一时间也看不出来怎么乱了,试特定的数字吧,看看a[0][1][2][3][4]对应什么b的什么就行了,然后发现没用,因为——
with tf.Session(graph=graph) as sess:
pred_sbbox, pred_mbbox, pred_lbbox = sess.run(
[return_tensors[1], return_tensors[2], return_tensors[3]],
feed_dict={ return_tensors[0]: image_data})
是在下输了!那就重组维度吧,打印出两个维度一看,openvino:(1, 6, 52, 52, 3),TensorFlow:(1, ?, ?, 3, 6),应该是52,52。这好办,用numpy的rollaxis把第1维滚动到最后一维就行。
好不容易跑通了,都快差点忘记自己要干什么了,对,看看速度——
Device | CPU | CPU | CPU | GPU | Pi |
---|---|---|---|---|---|
Environment | Tensorflow | OpenVINO | OpenVINO+NCS | OpenVINO | OpenVINO+NCS |
Average(ms) | 623 | 471 | 371 | 229 | 487 |
Up | 1 | 1.30 | 1.63 | 2.68 | 1.25 |
这。。。。远远达不到实时的要求啊,虽然速度有提升,但这也太少了吧。看来得找其他办法了——
整理了这么久,也写了快一天的博客,收获还是很大的。
cv2.imshow('Result', image)
还不够,会一闪而过,得加上cv2.waitKey(0)
,永远等待按键。YunYang1994 tensorflow-yolov3 ↩︎
OpenVINO™ toolkit Documentation ↩︎
utils3文件,提取码p2y8 ↩︎