1 OpenVINO概述
根据百度百科等资料,OpenVINO是英特尔推出的一款AI工具套件,可以用于快速部署AI应用和解决方案,支持计算机视觉的CNN网络结构超过150余种。它可以兼容多种主流的开源框架如PyTorch,Tensorflow,Keras,mxnet、Caffe和ONNX,并可以将上述开源框架训练好的模型,轻松的通过工具转换到OpenVINO框架上,并支持将预训练模型部署到英特尔的CPU、GPU和VPU上。
对于初步想体验的开发者来说,OpenVINO将AI模型部署到英特尔的CPU是非常方面的。OpenVINO套件还包含了图片处理包OpenCV和视频处理工具包Media SDK,用于处理图像视频解码,前处理和推理结果后处理等。一般来说,在做模型推理时,多数情况下需要进行前处理和后处理,前处理包含通道变换,大小变化,取均值和归一化等操作;而后处理需要将目标检测框等特征绘制到图像上,这些操作可以使用OpenVINO和OpenCV完成。
官网https://docs.openvino.ai/latest/index.html 给出OpenVINO支持的AI框架和硬件示意图:
另外,我们需要注意,不同的硬件环境,支持的模型以及输入和输出的精度是不同的,下面给出不同硬件支持的模型格式 (https://docs.openvino.ai/latest/openvino_docs_IE_DG_supported_plugins_Supported_Devices.html):
从上图可知,VPU 目前只支持FP16的模型格式。并在如下的硬件上进行了优化(Optimized for processors):
- 6th to 12th generation Intel® Core™ processors and Intel® Xeon® processors
- 3rd generation Intel® Xeon® Scalable processor (formerly code named Cooper Lake)
- Intel® Xeon® Scalable processor (formerly Skylake and Cascade Lake)
- Intel Atom® processor with support for Intel® Streaming SIMD Extensions 4.1 (Intel® SSE4.1)
- Intel Pentium® processor N4200/5, N3350/5, or N3450/5 with Intel® HD Graphics
- Intel® Iris® Xe MAX Graphics
- Intel® Neural Compute Stick 2
- Intel® Vision Accelerator Design with Intel® Movidius™ VPUs
2 Yolov5概述
Yolov5是 YOLO算法 (You Only Look Once)的第5个版本,YOLO可以说是当前实时对象检测算法中的佼佼者,它由Redmon等人在2016年的一篇论文中命名的,目前广泛应用于多种AI 目标对象检测场景中,可以非常方便的检测出图像中的对象类别,比如人、车、动物等。虽然在某些场景下, yolov4的算法推理精度优于目前的yolov5,但是yolov5的某些新特征却更加具有吸引力。比如,yolov5在检测平均精度降低不多的基础上,具有推理模型文件更小,训练时间和推理速度更短的特点,这样在对精度要求不高的情况下,采用yolov5在模型构建、模型部署等方面将更加方便,而且推理速度更快。
官网源码地址路径为: https://github.com/ultralytics/yolov5 ,在官网上也给出了yolov5 (6.0 releases)不同预训练的权重模型(YOLOv5s、YOLOv5m和 YOLOv5n 等)。为了适配 OpenVINO ,这里选择 yolov5 v5.0版本。这里可以从https://github.com/ultralytics/yolov5/releases/tag/v5.0 上下载权重模型文件和源码yolov5-5.0。
3 适配环境概述
- 操作系统 : Windows 10 ( Intel CPU )
- Python版本: Python 3.7.7
- OpenVINO Python Package : openvino 2021.4.2
- Yolov : yolov5-5.0
- netron 5.5.3
- onnx 1.10.2
- opencv-python : 4.5.4.60
关于OpenVINO套件,可以从官网进行下载w_openvino_toolkit_p_2021.4.752.exe。这里安装的路径为:
C:\Program Files (x86)\Intel\openvino_2021.4.752 。其内部目录结构如下所示:
另外,openvino_toolkit还需要安装其他依赖性(C++环境) ,这里安装了cmake-3.22.1-windows-x86_64.msi 和Visual Studo 2019 with MSBuild,可以通过安装Visual Studio社区版来进行完成,其中需要勾选C++桌面开发环境等组件。具体可以参考OpenVINO官网文档 :
https://docs.openvino.ai/latest/openvino_docs_install_guides_installing_openvino_windows.html。环境搭建完成后,可以执行如下CMD命令,来验证C++环境搭建是否成功:
#临时设置环境变量"C:\Program Files (x86)\Intel\openvino_2021\bin\setupvars.bat"# 切换目录cd C:\Program Files (x86)\Intel\openvino_2021\deployment_tools\model_optimizer\install_prerequisites # 安装依赖项install_prerequisites.bat #编译并运行Democd C:\Program Files (x86)\Intel\openvino_2021\deployment_tools\demo demo_security_barrier_camera.bat
正常编译后,可以输出如下界面:
推理过程输出信息如下所示:
4 Pytorch Yolov权重文件格式转换
用Visual Studio Code打开yolov5-5.0 目录,并修改models/common.py文件(约39行),将self.act = nn.SiLU() if act is True else (act if isinstance(act, nn.Module) else nn.Identity()) 修改为 self.act = nn.LeakyReLU(0.1, inplace=True),示例代码(注意第7行和8行)如下所示:
classConv(nn.Module): # Standard convolutiondef__init__(self, c1, c2, k=1, s=1, p=None, g=1, act=True): # ch_in, ch_out, kernel, stride, padding, groupssuper(Conv, self).__init__() self.conv=nn.Conv2d(c1, c2, k, s, autopad(k, p), groups=g, bias=False) self.bn=nn.BatchNorm2d(c2) # self.act = nn.SiLU() if act is True else (act if isinstance(act, nn.Module) else nn.Identity())self.act=nn.LeakyReLU(0.1, inplace=True)
修改models/export.py文件(约77行),将 opset_version=12 改成 opset_version=10 ,示意代码如下:
# ONNX exporttry: # import onnx 移动到 import torch之前,否则window上导出crashprint('\nStarting ONNX export with onnx %s...'%onnx.__version__) f=opt.weights.replace('.pt', '.onnx') # filenametorch.onnx.export(model, img, f, verbose=False, opset_version=10, input_names=['images'], output_names=['classes', 'boxes'] ifyisNoneelse ['output'], dynamic_axes={'images': {0: 'batch', 2: 'height', 3: 'width'}, # size(1,3,640,640)'output': {0: 'batch', 2: 'y', 3: 'x'}} ifopt.dynamicelseNone) # Checksonnx_model=onnx.load(f) # load onnx modelonnx.checker.check_model(onnx_model) # check onnx model# print(onnx.helper.printable_graph(onnx_model.graph)) # print a human readable modelprint('ONNX export success, saved as %s'%f) exceptExceptionase: print('ONNX export failure: %s'%e)
注意:在Windows操作系统上,import onnx 必须要先于 import torch,否则export.py导出onnx会crash掉。
导出onnx模型,可以参考如下代码:
"""Exports a YOLOv5 *.pt model to ONNX and TorchScript formatsUsage: $ export PYTHONPATH="$PWD" && python models/export.py --weights ./weights/yolov5s.pt --img 640 --batch 1"""importonnximportargparseimportsysimporttimesys.path.append('./') # to run '$ python *.py' files in subdirectoriesimporttorchimporttorch.nnasnnimportmodelsfrommodels.experimentalimportattempt_loadfromutils.activationsimportHardswish, SiLUfromutils.generalimportset_logging, check_img_sizefromutils.torch_utilsimportselect_deviceif__name__=='__main__': parser=argparse.ArgumentParser() parser.add_argument('--weights', type=str, default='./yolov5s.pt', help='weights path') # from yolov5/models/parser.add_argument('--img-size', nargs='+', type=int, default=[640, 640], help='image size') # height, widthparser.add_argument('--batch-size', type=int, default=1, help='batch size') parser.add_argument('--dynamic', action='store_true', help='dynamic ONNX axes') parser.add_argument('--grid', action='store_true', help='export Detect() layer grid') parser.add_argument('--device', default='cpu', help='cuda device, i.e. 0 or 0,1,2,3 or cpu') opt=parser.parse_args() opt.img_size*=2iflen(opt.img_size) ==1else1# expandprint(opt) set_logging() t=time.time() # Load PyTorch modeldevice=select_device(opt.device) model=attempt_load(opt.weights, map_location=device) # load FP32 modellabels=model.names# Checksgs=int(max(model.stride)) # grid size (max stride)opt.img_size= [check_img_size(x, gs) forxinopt.img_size] # verify img_size are gs-multiples# Inputimg=torch.zeros(opt.batch_size, 3, *opt.img_size).to(device) # image size(1,3,320,192) iDetection# Update modelfork, minmodel.named_modules(): m._non_persistent_buffers_set=set() # pytorch 1.6.0 compatibilityifisinstance(m, models.common.Conv): # assign export-friendly activationsifisinstance(m.act, nn.Hardswish): m.act=Hardswish() elifisinstance(m.act, nn.SiLU): m.act=SiLU() # elif isinstance(m, models.yolo.Detect):# m.forward = m.forward_export # assign forward (optional)model.model[-1].export=notopt.grid# set Detect() layer grid exporty=model(img) # dry run# ONNX exporttry: print('\nStarting ONNX export with onnx %s...'%onnx.__version__) f=opt.weights.replace('.pt', '.onnx') # filenametorch.onnx.export(model, img, f, verbose=False, opset_version=10, input_names=['images'], output_names=['classes', 'boxes'] ifyisNoneelse ['output'], dynamic_axes={'images': {0: 'batch', 2: 'height', 3: 'width'}, # size(1,3,640,640)'output': {0: 'batch', 2: 'y', 3: 'x'}} ifopt.dynamicelseNone) # Checksonnx_model=onnx.load(f) # load onnx modelonnx.checker.check_model(onnx_model) # check onnx model# print(onnx.helper.printable_graph(onnx_model.graph)) # print a human readable modelprint('ONNX export success, saved as %s'%f) exceptExceptionase: print('ONNX export failure: %s'%e) # Finishprint('\nExport complete (%.2fs). Visualize with https://github.com/lutzroeder/netron.'% (time.time() -t))
可以从https://github.com/ultralytics/yolov5/releases/download/v5.0/yolov5s.pt 下载权重文件,并改成yolov5s_v5.pt备用。然后支持如下命令:
#pip install openvino#pip install onnx#pip install netron# yolov5s_v5.pt --> yolov5s_v5.onnxpython models/export.py --weights yolov5s_v5.pt --img-size640
创建view_onnx.py 文件用于onnx模型预览,代码如下所示:
importnetronimportosimportargparsedefparse_opt(): parser=argparse.ArgumentParser() parser.add_argument('--weights', type=str, default='yolov5s.onnx', help='model.pt path(s)') opt=parser.parse_args() returnoptdefmain(opt): model_path=os.path.join(os.getcwd(),opt.weights) netron.start(model_path) if__name__=="__main__": opt=parse_opt() main(opt)
支持如下命令启动程序:
pythonview_onnx.py--weights'yolov5s_v5.onnx'# http://localhost:8080/
通过预览界面,找到模型结构上的3个输出节点,并向上找到第一个Conv结构,并记录名称备用,示意界面如下所示:
我这里记录的是 Conv_243 Conv_246 和 Conv_249 。并执行如下命令:
python'C:\Program Files (x86)\Intel\openvino_2021.4.752\deployment_tools\model_optimizer\mo.py'--input_modelyolov5s_v5.onnx-s255--reverse_input_channels--outputConv_243,Conv_246,Conv_249
输出如下信息:
ModelOptimizerarguments: Commonparameters: -PathtotheInputModel: D:\wangming\yolov5-5.0\yolov5s_v5.onnx-PathforgeneratedIR: D:\wangming\yolov5-5.0\. -IRoutputname: yolov5s_v5-Loglevel: ERROR-Batch: Notspecified, inheritedfromthemodel-Inputlayers: Notspecified, inheritedfromthemodel-Outputlayers: Conv_243,Conv_246,Conv_249-Inputshapes: Notspecified, inheritedfromthemodel-Meanvalues: Notspecified-Scalevalues: Notspecified-Scalefactor: 255.0-PrecisionofIR: FP32-Enablefusing: True-Enablegroupedconvolutionsfusing: True-Movemeanvaluestopreprocesssection: None-Reverseinputchannels: TrueONNXspecificparameters: -InferenceEnginefoundin: C:\Users\dev2021\AppData\Local\Programs\Python\Python37\lib\site-packages\openvinoInferenceEngineversion: 2021.4.2-3976-0943ed67223-refs/pull/539/headModelOptimizerversion: 2021.4.2-3974-e2a469a3450-releases/2021/4[ WARNING ] ModelOptimizerandInferenceEngineversionsdonomatch. [ WARNING ] ConsiderbuildingtheInferenceEnginePythonAPIfromsourcesorreinstallOpenVINO (TM) toolkitusing"pip install openvino==2021.4"[ SUCCESS ] GeneratedIRversion10model. [ SUCCESS ] XMLfile: D:\wangming\yolov5-5.0\yolov5s_v5.xml[ SUCCESS ] BINfile: D:\wangming\yolov5-5.0\yolov5s_v5.bin[ SUCCESS ] Totalexecutiontime: 16.97seconds.
导出成功则生成yolov5s_v5.bin和yolov5s_v5.xml文件,且Precision of IR格式为FP32。
5 Yolov5 OpenVINO 示例
新建一个yolov5_openvino_2021.4.py文件,其核心代码如下所示:
#!/usr/bin/env python""" Copyright (C) 2018-2019 Intel Corporation Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License."""from__future__importprint_function, divisionimportloggingimportosimportsysfromargparseimportArgumentParser, SUPPRESSfrommathimportexpasexpfromtimeimporttimeimportnumpyasnpimportcv2fromopenvino.inference_engineimportIENetwork, IECorelogging.basicConfig(format="[ %(levelname)s ] %(message)s", level=logging.INFO, stream=sys.stdout) log=logging.getLogger() yolov5_names= [ 'person', 'bicycle', 'car', 'motorcycle', 'airplane', 'bus', 'train', 'truck', 'boat', 'traffic light', 'fire hydrant', 'stop sign', 'parking meter', 'bench', 'bird', 'cat', 'dog', 'horse', 'sheep', 'cow', 'elephant', 'bear', 'zebra', 'giraffe', 'backpack', 'umbrella', 'handbag', 'tie', 'suitcase', 'frisbee', 'skis', 'snowboard', 'sports ball', 'kite', 'baseball bat', 'baseball glove', 'skateboard', 'surfboard', 'tennis racket', 'bottle', 'wine glass', 'cup', 'fork', 'knife', 'spoon', 'bowl', 'banana', 'apple', 'sandwich', 'orange', 'broccoli', 'carrot', 'hot dog', 'pizza', 'donut', 'cake', 'chair', 'couch', 'potted plant', 'bed', 'dining table', 'toilet', 'tv', 'laptop', 'mouse', 'remote', 'keyboard', 'cell phone', 'microwave', 'oven', 'toaster', 'sink', 'refrigerator', 'book', 'clock', 'vase', 'scissors', 'teddy bear', 'hair drier', 'toothbrush' ] defbuild_argparser(): parser=ArgumentParser(add_help=False) args=parser.add_argument_group('Options') args.add_argument('-h', '--help', action='help', default=SUPPRESS, help='Show this help message and exit.') args.add_argument("-m", "--model", help="Required. Path to an .xml file with a trained model.", required=True, type=str) args.add_argument("-i", "--input", help="Required. Path to an image/video file. (Specify 'cam' to work with ""camera)", required=True, type=str) args.add_argument("-l", "--cpu_extension", help="Optional. Required for CPU custom layers. Absolute path to a shared library with ""the kernels implementations.", type=str, default=None) args.add_argument("-d", "--device", help="Optional. Specify the target device to infer on; CPU, GPU, FPGA, HDDL or MYRIAD is"" acceptable. The sample will look for a suitable plugin for device specified. ""Default value is CPU", default="CPU", type=str) args.add_argument("--labels", help="Optional. Labels mapping file", default=None, type=str) args.add_argument("-t", "--prob_threshold", help="Optional. Probability threshold for detections filtering", default=0.5, type=float) args.add_argument("-iout", "--iou_threshold", help="Optional. Intersection over union threshold for overlapping ""detections filtering", default=0.4, type=float) args.add_argument("-ni", "--number_iter", help="Optional. Number of inference iterations", default=1, type=int) args.add_argument("-pc", "--perf_counts", help="Optional. Report performance counters", default=False, action="store_true") args.add_argument("-r", "--raw_output_message", help="Optional. Output inference results raw values showing", default=False, action="store_true") args.add_argument("--no_show", help="Optional. Don't show output", action='store_true') returnparserclassYoloParams: # ------------------------------------------- Extracting layer parameters ------------------------------------------# Magic numbers are copied from yolo samplesdef__init__(self, side): self.num=3#if 'num' not in param else int(param['num'])self.coords=4#if 'coords' not in param else int(param['coords'])self.classes=80#if 'classes' not in param else int(param['classes'])self.side=sideself.anchors= [10.0, 13.0, 16.0, 30.0, 33.0, 23.0, 30.0, 61.0, 62.0, 45.0, 59.0, 119.0, 116.0, 90.0, 156.0, 198.0, 373.0, 326.0] #if 'anchors' not in param else [float(a) for a in param['anchors'].split(',')]#self.isYoloV3 = False#if param.get('mask'):# mask = [int(idx) for idx in param['mask'].split(',')]# self.num = len(mask)# maskedAnchors = []# for idx in mask:# maskedAnchors += [self.anchors[idx * 2], self.anchors[idx * 2 + 1]]# self.anchors = maskedAnchors# self.isYoloV3 = True # Weak way to determine but the only one.deflog_params(self): params_to_print= {'classes': self.classes, 'num': self.num, 'coords': self.coords, 'anchors': self.anchors} [log.info(" {:8}: {}".format(param_name, param)) forparam_name, paraminparams_to_print.items()] defletterbox(img, size=(640, 640), color=(114, 114, 114), auto=True, scaleFill=False, scaleup=True): # Resize image to a 32-pixel-multiple rectangle https://github.com/ultralytics/yolov3/issues/232shape=img.shape[:2] # current shape [height, width]w, h=size# Scale ratio (new / old)r=min(h/shape[0], w/shape[1]) ifnotscaleup: # only scale down, do not scale up (for better test mAP)r=min(r, 1.0) # Compute paddingratio=r, r# width, height ratiosnew_unpad=int(round(shape[1] *r)), int(round(shape[0] *r)) dw, dh=w-new_unpad[0], h-new_unpad[1] # wh paddingifauto: # minimum rectangledw, dh=np.mod(dw, 64), np.mod(dh, 64) # wh paddingelifscaleFill: # stretchdw, dh=0.0, 0.0new_unpad= (w, h) ratio=w/shape[1], h/shape[0] # width, height ratiosdw/=2# divide padding into 2 sidesdh/=2ifshape[::-1] !=new_unpad: # resizeimg=cv2.resize(img, new_unpad, interpolation=cv2.INTER_LINEAR) top, bottom=int(round(dh-0.1)), int(round(dh+0.1)) left, right=int(round(dw-0.1)), int(round(dw+0.1)) img=cv2.copyMakeBorder(img, top, bottom, left, right, cv2.BORDER_CONSTANT, value=color) # add bordertop2, bottom2, left2, right2=0, 0, 0, 0ifimg.shape[0] !=h: top2= (h-img.shape[0])//2bottom2=top2img=cv2.copyMakeBorder(img, top2, bottom2, left2, right2, cv2.BORDER_CONSTANT, value=color) # add borderelifimg.shape[1] !=w: left2= (w-img.shape[1])//2right2=left2img=cv2.copyMakeBorder(img, top2, bottom2, left2, right2, cv2.BORDER_CONSTANT, value=color) # add borderreturnimgdefscale_bbox(x, y, height, width, class_id, confidence, im_h, im_w, resized_im_h=640, resized_im_w=640): gain=min(resized_im_w/im_w, resized_im_h/im_h) # gain = old / newpad= (resized_im_w-im_w*gain) /2, (resized_im_h-im_h*gain) /2# wh paddingx=int((x-pad[0])/gain) y=int((y-pad[1])/gain) w=int(width/gain) h=int(height/gain) xmin=max(0, int(x-w/2)) ymin=max(0, int(y-h/2)) xmax=min(im_w, int(xmin+w)) ymax=min(im_h, int(ymin+h)) # Method item() used here to convert NumPy types to native types for compatibility with functions, which don't# support Numpy types (e.g., cv2.rectangle doesn't support int64 in color parameter)returndict(xmin=xmin, xmax=xmax, ymin=ymin, ymax=ymax, class_id=class_id.item(), confidence=confidence.item()) defentry_index(side, coord, classes, location, entry): side_power_2=side**2n=location//side_power_2loc=location%side_power_2returnint(side_power_2* (n* (coord+classes+1) +entry) +loc) defparse_yolo_region(blob, resized_image_shape, original_im_shape, params, threshold): # ------------------------------------------ Validating output parameters ------------------------------------------ out_blob_n, out_blob_c, out_blob_h, out_blob_w=blob.shapepredictions=1.0/(1.0+np.exp(-blob)) assertout_blob_w==out_blob_h, "Invalid size of output blob. It sould be in NCHW layout and height should " \ "be equal to width. Current height = {}, current width = {}" \ "".format(out_blob_h, out_blob_w) # ------------------------------------------ Extracting layer parameters -------------------------------------------orig_im_h, orig_im_w=original_im_shaperesized_image_h, resized_image_w=resized_image_shapeobjects=list() side_square=params.side*params.side# ------------------------------------------- Parsing YOLO Region output -------------------------------------------bbox_size=int(out_blob_c/params.num) #4+1+num_classesforrow, col, ninnp.ndindex(params.side, params.side, params.num): bbox=predictions[0, n*bbox_size:(n+1)*bbox_size, row, col] x, y, width, height, object_probability=bbox[:5] class_probabilities=bbox[5:] ifobject_probability<threshold: continuex= (2*x-0.5+col)*(resized_image_w/out_blob_w) y= (2*y-0.5+row)*(resized_image_h/out_blob_h) ifint(resized_image_w/out_blob_w) ==8&int(resized_image_h/out_blob_h) ==8: #80x80, idx=0elifint(resized_image_w/out_blob_w) ==16&int(resized_image_h/out_blob_h) ==16: #40x40idx=1elifint(resized_image_w/out_blob_w) ==32&int(resized_image_h/out_blob_h) ==32: # 20x20idx=2width= (2*width)**2*params.anchors[idx*6+2*n] height= (2*height)**2*params.anchors[idx*6+2*n+1] class_id=np.argmax(class_probabilities) confidence=object_probabilityobjects.append(scale_bbox(x=x, y=y, height=height, width=width, class_id=class_id, confidence=confidence, im_h=orig_im_h, im_w=orig_im_w, resized_im_h=resized_image_h, resized_im_w=resized_image_w)) returnobjectsdefintersection_over_union(box_1, box_2): width_of_overlap_area=min(box_1['xmax'], box_2['xmax']) -max(box_1['xmin'], box_2['xmin']) height_of_overlap_area=min(box_1['ymax'], box_2['ymax']) -max(box_1['ymin'], box_2['ymin']) ifwidth_of_overlap_area<0orheight_of_overlap_area<0: area_of_overlap=0else: area_of_overlap=width_of_overlap_area*height_of_overlap_areabox_1_area= (box_1['ymax'] -box_1['ymin']) * (box_1['xmax'] -box_1['xmin']) box_2_area= (box_2['ymax'] -box_2['ymin']) * (box_2['xmax'] -box_2['xmin']) area_of_union=box_1_area+box_2_area-area_of_overlapifarea_of_union==0: return0returnarea_of_overlap/area_of_uniondefmain(): args=build_argparser().parse_args() # ------------- 1. Plugin initialization for specified device and load extensions library if specified -------------log.info("Creating Inference Engine...") ie=IECore() ifargs.cpu_extensionand'CPU'inargs.device: ie.add_extension(args.cpu_extension, "CPU") # -------------------- 2. Reading the IR generated by the Model Optimizer (.xml and .bin files) --------------------model=args.modellog.info(f"Loading network:\n\t{model}") net=ie.read_network(model=model) # ---------------------------------- 3. Load CPU extension for support specific layer ------------------------------# if "CPU" in args.device:# supported_layers = ie.query_network(net, "CPU")# not_supported_layers = [l for l in net.layers.keys() if l not in supported_layers]# if len(not_supported_layers) != 0:# log.error("Following layers are not supported by the plugin for specified device {}:\n {}".# format(args.device, ', '.join(not_supported_layers)))# log.error("Please try to specify cpu extensions library path in sample's command line parameters using -l "# "or --cpu_extension command line argument")# sys.exit(1)assertlen(net.input_info.keys()) ==1, "Sample supports only YOLO V3 based single input topologies"# ---------------------------------------------- 4. Preparing inputs -----------------------------------------------log.info("Preparing inputs") input_blob=next(iter(net.input_info)) # Defaulf batch_size is 1net.batch_size=1# Read and pre-process input imagesn, c, h, w=net.input_info[input_blob].input_data.shapeifargs.labels: withopen(args.labels, 'r') asf: labels_map= [x.strip() forxinf] else: labels_map=Noneinput_stream=0ifargs.input=="cam"elseargs.inputis_async_mode=Truecap=cv2.VideoCapture(input_stream) number_input_frames=int(cap.get(cv2.CAP_PROP_FRAME_COUNT)) number_input_frames=1ifnumber_input_frames!=-1andnumber_input_frames<0elsenumber_input_frameswait_key_code=1# Number of frames in picture is 1 and this will be read in cycle. Sync mode is default value for this caseifnumber_input_frames!=1: ret, frame=cap.read() is_async_mode=Falseelse: is_async_mode=Falsewait_key_code=0# ----------------------------------------- 5. Loading model to the plugin -----------------------------------------log.info("Loading model to the plugin") exec_net=ie.load_network(network=net, num_requests=2, device_name=args.device) cur_request_id=0next_request_id=1render_time=0parsing_time=0# ----------------------------------------------- 6. Doing inference -----------------------------------------------log.info("Starting inference...") print("To close the application, press 'CTRL+C' here or switch to the output window and press ESC key") print("To switch between sync/async modes, press TAB key in the output window") cur_frame_count=0fps=28whilecap.isOpened(): # Here is the first asynchronous point: in the Async mode, we capture frame to populate the NEXT infer request# in the regular mode, we capture frame to the CURRENT infer requestifis_async_mode: ret, next_frame=cap.read() else: ret, frame=cap.read() ifnotret: breakcur_frame_count+=1ifcur_frame_count%fps!=0 : continueifis_async_mode: request_id=next_request_idin_frame=letterbox(frame, (w, h)) else: request_id=cur_request_idin_frame=letterbox(frame, (w, h)) in_frame0=in_frame# resize input_frame to network sizein_frame=in_frame.transpose((2, 0, 1)) # Change data layout from HWC to CHWin_frame=in_frame.reshape((n, c, h, w)) # Start inferencestart_time=time() exec_net.start_async(request_id=request_id, inputs={input_blob: in_frame}) det_time=time() -start_time# Collecting object detection resultsobjects=list() ifexec_net.requests[cur_request_id].wait(-1) ==0: output=exec_net.requests[cur_request_id].output_blobsstart_time=time() forlayer_name, out_blobinoutput.items(): layer_params=YoloParams(side=out_blob.buffer.shape[2]) log.info("Layer {} parameters: ".format(layer_name)) layer_params.log_params() objects+=parse_yolo_region(out_blob.buffer, in_frame.shape[2:], #in_frame.shape[2:], layer_params,frame.shape[:-1], layer_params, args.prob_threshold) parsing_time=time() -start_time# Filtering overlapping boxes with respect to the --iou_threshold CLI parameterobjects=sorted(objects, key=lambdaobj : obj['confidence'], reverse=True) foriinrange(len(objects)): ifobjects[i]['confidence'] ==0: continueforjinrange(i+1, len(objects)): ifintersection_over_union(objects[i], objects[j]) >args.iou_threshold: objects[j]['confidence'] =0# Drawing objects with respect to the --prob_threshold CLI parameterobjects= [objforobjinobjectsifobj['confidence'] >=args.prob_threshold] iflen(objects) andargs.raw_output_message: log.info("\nDetected boxes for batch {}:".format(1)) log.info(" Class ID | Confidence | XMIN | YMIN | XMAX | YMAX | COLOR ") origin_im_size=frame.shape[:-1] print(origin_im_size) forobjinobjects: # Validation bbox of detected objectifobj['xmax'] >origin_im_size[1] orobj['ymax'] >origin_im_size[0] orobj['xmin'] <0orobj['ymin'] <0: continue# color = (int(min(obj['class_id'] * 12.5, 255)),# min(obj['class_id'] * 7, 255), min(obj['class_id'] * 5, 255))color= (0,255,0) det_label=labels_map[obj['class_id']] iflabels_mapandlen(labels_map) >=obj['class_id'] else \ str(obj['class_id']) ifargs.raw_output_message: log.info( "{:^9} | {:10f} | {:4} | {:4} | {:4} | {:4} | {} ".format(det_label, obj['confidence'], obj['xmin'], obj['ymin'], obj['xmax'], obj['ymax'], color)) cv2.rectangle(frame, (obj['xmin'], obj['ymin']), (obj['xmax'], obj['ymax']), color, 2) cls_name=yolov5_names[int(det_label)] cv2.putText(frame, ""+cls_name+' '+str(round(obj['confidence'] *100, 1)) +' %', (obj['xmin'], obj['ymin'] -7), cv2.FONT_HERSHEY_COMPLEX, 0.6, color, 1) # Draw performance stats over frameinf_time_message="Inference time: N\A for async mode"ifis_async_modeelse \ "Inference time: {:.3f} ms".format(det_time*1e3) render_time_message="OpenCV rendering time: {:.3f} ms".format(render_time*1e3) async_mode_message="Async mode is on. Processing request {}".format(cur_request_id) ifis_async_modeelse \ "Async mode is off. Processing request {}".format(cur_request_id) parsing_message="YOLO parsing time is {:.3f} ms".format(parsing_time*1e3) cv2.putText(frame, inf_time_message, (15, 15), cv2.FONT_HERSHEY_COMPLEX, 0.5, (0, 255, 0), 1) cv2.putText(frame, render_time_message, (15, 45), cv2.FONT_HERSHEY_COMPLEX, 0.5, (255, 0, 0), 1) cv2.putText(frame, async_mode_message, (10, int(origin_im_size[0] -20)), cv2.FONT_HERSHEY_COMPLEX, 0.5, (10, 10, 200), 1) cv2.putText(frame, parsing_message, (15, 30), cv2.FONT_HERSHEY_COMPLEX, 0.5, (0, 0, 255), 1) start_time=time() ifnotargs.no_show: cv2.imshow("DetectionResults", frame) render_time=time() -start_timeifis_async_mode: cur_request_id, next_request_id=next_request_id, cur_request_idframe=next_frameifnotargs.no_show: key=cv2.waitKey(wait_key_code) # ESC keyifkey==27: break# Tab keyifkey==9: exec_net.requests[cur_request_id].wait() is_async_mode=notis_async_modelog.info("Switched to {} mode".format("async"ifis_async_modeelse"sync")) cv2.destroyAllWindows() if__name__=='__main__': sys.exit(main() or0)
支持如下命令:
pythonyolov5_openvino_2021.4.py-idemo.mp4-myolov5s_v5.xml# 推理单张图片,则需要修改yolov5_openvino_2021.4.py# cur_frame_count += 1 # if cur_frame_count % fps != 0 :# continuepythonyolov5_openvino_2021.4.py-idata/images/zidane.jpg-myolov5s_v5.xml
输出界面如下所示:
控制台输出信息如下所示:
[ INFO ] CreatingInferenceEngine... [ INFO ] Loadingnetwork: yolov5s_v5.xml[ INFO ] Preparinginputs[ INFO ] Loadingmodeltotheplugin[ INFO ] Startinginference... Toclosetheapplication, press'CTRL+C'hereorswitchtotheoutputwindowandpressESCkeyToswitchbetweensync/asyncmodes, pressTABkeyintheoutputwindow[ INFO ] LayerConv_243parameters: [ INFO ] classes : 80[ INFO ] num : 3[ INFO ] coords : 4[ INFO ] anchors : [10.0, 13.0, 16.0, 30.0, 33.0, 23.0, 30.0, 61.0, 62.0, 45.0, 59.0, 119.0, 116.0, 90.0, 156.0, 198.0, 373.0, 326.0] [ INFO ] LayerConv_246parameters: [ INFO ] classes : 80[ INFO ] num : 3[ INFO ] coords : 4[ INFO ] anchors : [10.0, 13.0, 16.0, 30.0, 33.0, 23.0, 30.0, 61.0, 62.0, 45.0, 59.0, 119.0, 116.0, 90.0, 156.0, 198.0, 373.0, 326.0] [ INFO ] LayerConv_249parameters: [ INFO ] classes : 80[ INFO ] num : 3[ INFO ] coords : 4[ INFO ] anchors : [10.0, 13.0, 16.0, 30.0, 33.0, 23.0, 30.0, 61.0, 62.0, 45.0, 59.0, 119.0, 116.0, 90.0, 156.0, 198.0, 373.0, 326.0] (720, 1280)
参考如下文档: