OpenVINO整活(一) 输入分辨率

OpenVINO分为转换与部署两个部分,如下图所示

在转换步中,需要将输入模型序列化后传入OpenVINO的MOModel Optimizer工具对模型进行优化,以得到IR
Intermediate Representation中间表示,详细的内部行为可以参考Model Optimizer Developer Guide。接着,在部署步中,我们使用OpenVINO提供的Python或者CPP的api,进行编程以达到我们想要的结果:1) 载入ir文件, 2) 建立ieInference Engine推理引擎, 3) 生成执行对象ExecutableNetwork, 载入不同的plugin,以支持特定的硬件(CPU、GPU等等),详细行为可参考Inference Engine Developer Guide。

对于转换步,针对"输入模型序列化"我就产生了疑问,OpenVINO所使用的序列化后进行ir转换的模型,它是一个类似静态图的东西,序列化后我再转换,那岂不就是我只能支持输入静态分辨率了?要使用OpenVINO化的模型进行推理,任意输入就得按照序列化时的分辨率来推理,岂不是很僵硬。难道,OpenVINO的推理,就得先插值,再推理,最后再插值回原分辨率?

这里就直接给结论了,OpenVINO是支持动态输入的,并且在它的手册里,也有相应的段落Using Shape Inference,乍看起来很简单,但实则暗藏坑点,这里我也直接给出坑点来,CPP api是无法全面支持OpenVINO reshape操作的:

  1. Python api 全面支持reshape操作,不会出错
  2. CPP api 某些简单网络支持,我推测还是scale的精度问题,参考我之前的日志PyTorch转ONNX之F.interpolate,从4到9,python搞得定,cpp估计就搞不定了,例如就会报如下的错,也是网络内部的upsample的问题,所以可以在2) 建立ie引擎的时候,加入resize层,也就是在ir外部添加一个处理,而不是reshape整个ir

那么,下面就是两部分的详细说明

Python api

由于Python不像CPP,在精度和内存分配上,我们都不需要担心,所以完全可以使用OpenVINO自带的reshape操作,将整个ir调整到适合的输入分辨率。

这里需要注意两点,一是在使用mo工具进行ir转换的时候,需要使用--keep_shape_ops,二是在部署的时候,需要在"3) 生成ExecutableNetwork对象"前使用reshape。

一、使用mo转换ir

--keep_shape_ops[ Experimental feature ] Enables `Shape` operationwith all children keeping. This feature makes modelreshapable in Inference Engine

详细地,可以参考When to Specify --keep_shape_ops Command Line Parameter,mo工具会保留内部的分辨率变化的图结构,实话说这部分保留了什么样的图结构,我不清楚,我猜是像onnx那样的scale值,onnx是通过scale值来计算输入与输出的分辨率大小,有可能,--keep_shape_ops取消了原本scale的const类型?

二、使用python api进行推理

这里我给一个示例代码,这里也是修改自OpenVINO提供的deployment_tools/inference_engine/samples/python里面的,我把注释和提示信息打印了出来,方便大家理解。

# model config
model_xml = 'your_model.xml'
model_bin = 'your_model.bin'# Read IR
print("Creating Inference Engine...")
ie = IECore()
print("Loading network files:\n\t{}\n\t{}".format(model_xml, model_bin))
net = ie.read_network(model=model_xml, weights=model_bin)# Check params in IR
# 检查IR模型是否是支持CPU硬件的
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:print("Following layers are not supported by the plugin for specified device {}:\n {}".format('CPU', ', '.join(not_supported_layers)))print("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)
# 检查输入层的维度,这里说明是一个输入
assert len(net.inputs.keys()) == 1, "Sample supports only 1 input topologies -> {}".format(len(net.inputs.keys()))
# xml里面“注册”的输入的名字
required_input_keys = {'input'}
assert required_input_keys == set(net.inputs.keys()), \'Demo supports only topologies with the following input keys: {}'.format(', '.join(required_input_keys))
# 检查输出层的维度,这里有两个输出
assert len(net.outputs) == 2, "Sample supports only single output topologies"
# xml里面“注册”的输出的名字
required_output_keys = {'boxes', 'scores'}
assert required_output_keys.issubset(net.outputs.keys()), \'Demo supports only topologies with the following output keys: {}'.format(', '.join(required_output_keys))# Read and pre-process input images
n, c, h, w = net.inputs['input'].shape
print("input_blob NxCxHxW is {}x{}x{}x{}".format(n, c, h, w))# Reshape IR
# 读入输入图像的分辨率
input_size = cv2.imread("your_input_image.png").shape[:-1]
net.reshape({'input': (1, 3, input_size[0], input_size[1])})
n, c, h, w = net.inputs['input'].shape
print("input_blob NxCxHxW is resized to {}x{}x{}x{}".format(n, c, h, w))# Loading model to the plugin
# 生成执行对象ExecutableNetwork
print("Loading model to the plugin")
exec_net = ie.load_network(network=net, device_name='CPU')# Inference
print("Starting inference in synchronous mode")
for j in range(len(img_list)):img_path = img_list[j]ori_img  = cv2.imread(img_path)img = cv2.cvtColor(ori_img, cv2.COLOR_BGR2RGB)img = np.array(img, dtype=np.float32)img = img.transpose((2, 0, 1))  # Change data layout from HWC to CHWimg = img[np.newaxis, :, :, :]  # 整成1CHW# Start sync inferenceprint('[{}/{}]'.format(j, len(img_list)))res = exec_net.infer(inputs={'input': img})# Get resultsboxes  = res['boxes']scores = res['scores']# Post-processing

这里说明下“注册”是个啥意思,就是输入输出层的名字,实际上静态图里面,各层都是有类似“conv35”——“层类型-层的id”这样的名字,如果在IR转换的时候不规定,就是默认“conv35”的名称,规定层的名称,就是“注册”,我感觉这样来形容更加贴切一点。

CPP api

一、使用CPP api进行推理

模型转换那里是一样的,因此就不再赘述。

按理说,流程应该与“Python api推理”的流程是一致的,在文档Using Shape Inference的Usage of Reshape Method中提到CPP的流程如下所示:

InferenceEngine::Core core;
// ------------- 0. Read IR and image ----------------------------------------------
CNNNetwork network = core.ReadNetwork("path/to/IR/xml");
cv::Mat image = cv::imread("path/to/image");
// ---------------------------------------------------------------------------------
// ------------- 1. Collect the map of input names and shapes from IR---------------
auto input_shapes = network.getInputShapes();
// ---------------------------------------------------------------------------------
// ------------- 2. Set new input shapes -------------------------------------------
std::string input_name;
SizeVector input_shape;
std::tie(input_name, input_shape) = *input_shapes.begin(); // let's consider first input only
input_shape[0] = batch_size; // set batch size to the first input dimension
input_shape[2] = image.rows; // changes input height to the image one
input_shape[3] = image.cols; // changes input width to the image one
input_shapes[input_name] = input_shape;
// ---------------------------------------------------------------------------------
// ------------- 3. Call reshape ---------------------------------------------------
network.reshape(input_shapes);
// ---------------------------------------------------------------------------------
...
// ------------- 4. Loading model to the device ------------------------------------
std::string device = "CPU";
ExecutableNetwork executable_network = core.LoadNetwork(network, device);
// ---------------------------------------------------------------------------------

虽然会报之前讲到的错,因此我觉得这条reshape路是不容易走通的,那么是不是就卡关了呢?实际上是有解的,OpenVINO提供在"3) 生成ExecutableNetwork对象"前添加一个类似resize层的东西,这样就不会变动IR结构,而且使用OpenVINO提供的内建的resize操作,比自己在输入OpenVINO前进行自己的resize操作,我感觉应该是要快一点,如果画成图的话,就如下图所示,我们可以使用OpenVINO优化后的resize操作。

using namespace InferenceEngine;
Core ie;
string cnnNetworkXmlPath;
// ------------- 0. Read IR ----------------------------------------------
// 读取xml与bin
// ie默认读入与xml文件同名同路径的bin文件
// 例如,你的xml路径为/data/your_model.xml,那么ie会默认读取/data/your_model.bin
auto cnnNetwork = ie.ReadNetwork(cnnNetworkXmlPath);
// -----------------------------------------------------------------------
// ------------- 1. Collect the map of input names and shapes from IR-----
// 获取网络的输入信息
InputsDataMap inputInfo(cnnNetwork.getInputsInfo());
for (const auto & inputInfoItem : inputInfo)
{if (inputInfoItem.second->getTensorDesc().getDims().size() == 4) {  // 确保输入一定是4维,包含N、C、H、WimageInputName = inputInfoItem.first;// 设置输入精度为uint8,这样CPU占用要小一些inputInfoItem.second->setPrecision(Precision::U8);// 设置输入的维度格式,为NCHWinputInfoItem.second->getInputData()->setLayout(Layout::NCHW);// 添加前处理resize操作,并且这个resize层是双线性插值的inputInfoItem.second->getPreProcess().setResizeAlgorithm(ResizeAlgorithm::RESIZE_BILINEAR);} else {throw std::logic_error("Unsupported " +std::to_string(inputInfoItem.second->getTensorDesc().getDims().size()) + "D ""input layer '" + inputInfoItem.first + "'. ""Only 4D input layers are supported");}
}
// -----------------------------------------------------------------------
// ------------- 4. Loading model to the device --------------------------
std::string device = "CPU";
ExecutableNetwork executable_network = ie.LoadNetwork(network, device);

上述操作就是为前处理添加一个resize操作层,实际上这个PreProcess是支持很多操作的,例如归一化、减去均值等等,相当于就是在mo转ir的时候,插入的操作,大家可以查看相应的文档InferenceEngine::PreProcessInfo Class Reference看到它支持的各种操作。

好了,以上就是全部内容,在接下来的时间里,我应该会继续更新模型部署的内容,比如ssd的部署啊,trt的量化啊什么的,openvino的量化我也想试一试,奈何经历不够,又要吃饭,又想玩。

OpenVINO整活(一) 输入分辨率相关推荐

  1. 炉石整活拔线方法_酒馆战棋:整活如何简单“拔线”?瓦娘在线教学,却3本得死神?...

    大家好!我是炉石闲说,为您分享炉石魔兽趣闻趣事! 在酒馆战棋中,在目前游戏规则允许的条件下,整活作为一个对网络速度.电脑配置尤其是对玩家手速和大局观要求比较高的技术,整活通常在逆境中能帮你逆转局势,顺 ...

  2. 中科大发4万封钓鱼邮件给师生“免费送月饼”,网友:学校太会“整活”了......

    点击上方"视学算法",选择加"星标"或"置顶" 重磅干货,第一时间送达 本文来源:整理自上游新闻记者 赵映骥 实习生 彭星/文.知乎.募格学 ...

  3. C语言 system函数超简单讲解+整活

    在CSDN查(抄)代码的时候经常会遇到有大佬写出 system("pause") 这个语句. 后来又接触到 system("cls") 这使我非常好奇,于是查了 ...

  4. 笑不活了!院士带领课题组硕博“整活”,科研版「苦行僧」火出圈!

    点击上方"视学算法",选择加"星标"或"置顶" 重磅干货,第一时间送达 本文募格学术整理.参考资料:中国科学报.澎湃新闻.王晓东课题组视频等 ...

  5. 炉石整活拔线方法_炉石传说:采访仰天莫笑——黄金总决赛再度捧杯后的变化与成长...

    在前不久结束的黄金总决赛酒馆战棋组的比赛中,象征着冠军的金炉石和金钥匙由首届黄金公开赛棋王仰天莫笑捧起. 这位在上海站首度夺冠的选手又一次在上海证明了自己,而在之前的采访中他提到自己几乎不会整活,甚至 ...

  6. 整活插件 炉石传说_炉石传说:一顿操作猛如虎,定睛一看原地杵,会长整活被死人抬走...

    玩游戏找热点,点击关注不迷路 在酒馆战棋中,要说当下最强的一套吃鸡体系,那肯定是整活流了.这套体系也是玩家们经过了长久的摸索逐渐形成了一套完整的流派.这套流派的最大优势就是一旦整活成功,可以说吃鸡相当 ...

  7. Java黑皮书课后题第2章:*2.22(金融应用:货币单位)改写程序清单2-10,解决将double转int可能会造成精度损失问题。以整数值作为输入,其最后两位代表的是美分币值

    *2.22(金融应用:货币单位)改写程序清单2-10,解决将double转int可能会造成精度损失问题.以整数值作为输入,其最后两位代表的是美分币值 题目 题目概述 举例 程序清单2-10 修改思路 ...

  8. 稚晖君又整活啦!基于全志F1C200s的超迷你

    转自全志在线 B站最强小电视(基于全志H3) 钢铁侠机械臂 曾经靠着"B站最强小电视"以及号称"钢铁侠机械臂"等硬核产品出圈的稚晖君又双叒叕来整活啦! 稚晖君B ...

  9. 整活插件 炉石传说_酒馆战旗整活插件 免安装版

    炉石传说酒馆战旗整活插件是一款专门用来帮助玩家跳过炉石传说战斗动画的工具.炉石传说中的战旗模式会在匹配成功后播放一段战斗动画,等到动画播放完毕才会正式进入选牌阶段开始游戏,而很多经常玩战旗模式的玩家早 ...

最新文章

  1. java调python画图_Python Matplotlib plot 绘图
  2. C/C++ 如何劫持别人家的命令||函数||程序(只能对于window而言)
  3. 睡眠音频分割及识别问题(一)
  4. java中计时器的用法Timer和TimerTask的用法__java中利用Timer与TImerTask 计时器间隔执行任务...
  5. 我用python远程探查女友每天的网页访问记录,她不愧是成年人!
  6. 【BZOJ3675】【Apio2014】序列分割
  7. 关于shell读取文件打印时展开通配符
  8. 我要自学网access教程百度云_我要自学网教程资源汇总
  9. 王曙燕c语言答案,c语言程序设计王曙燕课后题答案
  10. mysql身份证校验码_MySql整理篇之身份证提取生日性别
  11. html设置图片为部分背景颜色,设置HTML的一个部分作为一个不同的背景颜色
  12. Linux 磁盘管理 高级篇 -- quota,RAID,LVM
  13. Your application has presented a UIAlertController of style UIAlertControllerStyleActionSheet.
  14. 计算机选购知识,计算机知识讲座(9):硬盘的选购
  15. NPOI导出excel设置打印为A4纸张
  16. 在线Java 动态运行Java源代码-执行器
  17. Ogre procedural 几何图元库
  18. 将Maven配置到Eclipse中
  19. Tekton实践篇-Tekton和Argocd的梦幻联动
  20. (半成品)Excel文件按要求导出至模板excel中(含配套数据)

热门文章

  1. Java岗大厂面试百日冲刺【Day46】— Linux2 (日积月累,每日三题)
  2. 使用vue写一个计时器
  3. 《麦田里的守望者》谁又不是以过来人的身份,来做一个麦田里的守望者呢?
  4. C primer plus 复习题答案(上)
  5. TikTok怎么运营带货
  6. python结束子进程_如何清除python中的子进程
  7. Cocos Creator 入门笔记
  8. 学习编程,应该从哪里开始学习呢?
  9. 面试之算法知识点总结
  10. 产品经理就业喜报:沉舟侧畔终迎万木春