专栏首页CNNOpenVINO运行Tensorflow模型
原创

OpenVINO运行Tensorflow模型

请先阅读我的上一篇文章《Visual Studio 2017 配置OpenVINO开发环境》,在VS2017中配置好OpenVINO环境。

1 模型转换

1.1安装模型转换工具

打开conda控制台,创建虚拟环境vino

conda create -n vino python=3.6

创建完成后,执行activate vino。然后安装OpenVINO模型转换工具,具体命令如下:

> activate vino
> cd E:\OpenVINO\openvino_2019.3.334\deployment_tools\model_optimizer
> pip install -r requirements_tf.txt

1.2 模型转换

MobileNet为例,前往https://github.com/tensorflow/models/blob/master/research/slim/nets/mobilenet_v1.md下载MobileNet_v1_1.0_224模型,解压到目录E:\model后,对mobilenet_v1_1.0_224_frozen.pb执行如下命令完成模型转换:

python E:\OpenVINO\openvino_2019.3.334\deployment_tools\model_optimizer\mo_tf.py --input_model mobilenet_v1_1.0_224_frozen.pb --input_shape [1,224,224,3] --output MobilenetV1/Logits/Conv2d_1c_1x1/Conv2D --mean_values [127.5,127.5,127.5] --scale_values [127.5,127.5,127.5]

参数介绍:

--input_model :指定输入模型路径
--input_shape :指定模型的输入Tensor的shape,如果不指定,则会自动从pb中读取
--output :指定输出节点名称,如果不指定,会自动从图中提取。注意,这里由于openVINO不支持squeeze层,所以我们主动指定squeeze的上一层即:MobilenetV1/Logits/Conv2d_1c_1x1/Conv2D,获取每一层名称的方法:可以先不指定output,会自动导出xml,从xml中即可看到每一层名称。
--scale_values :指定数据预处理的scale系数 
--mean_values: 指定数据预处理的mean系数

除了上面参数外,还有一些其他常用的参数:

--data_type: 指定计算类型,可以选择全浮点和半浮点,可选参数为:{FP16,FP32,half,float}

注意,scale_values参数和mean_values参数一般用于输入Tensor预处理,更常见的就是归一化。假设输入Tensor名称为in_tensor,经过预处理后,输出Tensor为out_tensor,其计算公式如下:

out_tensor = (in_tensor-mean_values)/scale_values 

例如,需要将输入归一化为-1,1,则mean_values取值为127.5,127.5,127.5且scale_values取值为127.5,127.5,127.5

完成后,在E:\model目录中生成如下三个文件:

模型转换

其中bin文件是模型参数,xml文件是网络结构,mapping文件是模型转换前后计算节点映射关系。我们主要用binxml文件。

注意,如果转换过程中出错了,可以尝试卸载Tenorflow,可能是因为Tensorflow版本问题,改为Tensorflow1.14-cpu版本,笔者这边使用1.14-cpu版本没有问题。

2 VS2017运行

2.1 环境配置

主要用到OpenVINO和OpenCV环境,OpenCV用于读取图片,OpenVINO用于运行模型。

参考我的上一篇文章【Visual Studio 2017 配置OpenVINO开发环境】配置好openVINO环境。 参考我的另一篇文章【OpenCV 3.2.0 + opencv_contrib+VS2017】配置好OpenCV环境。

注意:如果懒得配置,可以从附件中下载笔者已经搭建好的环境,可直接用VS2017打开运行

2.2 代码编写

E:\model拷贝到项目根目录,输入以下代码。

#include <inference_engine.hpp> 
#include <iostream>
#include <string> 
#include <vector>
#include <opencv2/opencv.hpp>
using namespace InferenceEngine;
using namespace std;

string inputName;
string outputName;
InferRequest inferReq;
vector<wstring> labels;
//初试化模型相关参数
void initModel(string xml,string bin,string plugin="plugins.xml") { 
	try {
		Core ie(plugin);
		CNNNetReader network_reader;
		network_reader.ReadNetwork(xml);
		network_reader.ReadWeights(bin);
		network_reader.getNetwork().setBatchSize(1);
		CNNNetwork network = network_reader.getNetwork();
		InputInfo::Ptr input_info = network.getInputsInfo().begin()->second;
		inputName = network.getInputsInfo().begin()->first;

		input_info->getPreProcess().setResizeAlgorithm(RESIZE_BILINEAR);
		input_info->setLayout(Layout::NCHW);
		input_info->setPrecision(Precision::U8);

		DataPtr output_info = network.getOutputsInfo().begin()->second;
		outputName = network.getOutputsInfo().begin()->first;

		output_info->setPrecision(Precision::FP32);

		ExecutableNetwork executable_network = ie.LoadNetwork(network, "CPU");

		inferReq = executable_network.CreateInferRequest();
	}catch (const std::exception & ex) {
		std::cerr << ex.what() << std::endl; 
	}
}
//Mat 转Blob
void  matU8ToBlob(const cv::Mat& orig_image, InferenceEngine::Blob::Ptr& blob, int batchIndex=0) {
	InferenceEngine::SizeVector blobSize = blob->getTensorDesc().getDims();
	const size_t width = blobSize[3];
	const size_t height = blobSize[2];
	const size_t channels = blobSize[1];
	uint8_t* blob_data = blob->buffer().as<uint8_t*>();

	cv::Mat resized_image(orig_image);
	if (static_cast<int>(width) != orig_image.size().width ||
		static_cast<int>(height) != orig_image.size().height) {
		cv::resize(orig_image, resized_image, cv::Size(width, height));
	}

	int batchOffset = batchIndex * width * height * channels;

	for (size_t c = 0; c < channels; c++) {
		for (size_t h = 0; h < height; h++) {
			for (size_t w = 0; w < width; w++) {
				blob_data[batchOffset + c * width * height + h * width + w] =
					resized_image.at<cv::Vec3b>(h, w)[c];
			}
		}
	}
}
//读取label
void readLabel(string labelPath)
{
	std::wstring_convert<std::codecvt_utf8<wchar_t>> conv;
	ifstream in(labelPath.c_str());
	string line;
	if (in) { // 有该文件 
		while (getline(in, line)) { // line中不包括每行的换行符 
			wstring wb = conv.from_bytes(line);
			labels.push_back(wb);
		} 
	} else { // 没有该文件 
		cout << "no such file:" << labelPath << endl;
	} 
}
//前向计算
wstring infer(cv::Mat rgb,float& rtP) {
	Blob::Ptr imgBlob = inferReq.GetBlob(inputName);
	matU8ToBlob(rgb, imgBlob);
	inferReq.Infer();
	Blob::Ptr output = inferReq.GetBlob(outputName);
	float* logits = output->buffer().as<InferenceEngine::PrecisionTrait<InferenceEngine::Precision::FP32>::value_type*>();

	int maxIdx = 0;
	float maxP = 0;
	int nclasses = labels.size();//1001类
	float sum = 1;
	//softmax
	for (int i = 0; i < nclasses; i++) {
		logits[i] = exp(logits[i]);
		sum = sum + logits[i];
		if (logits[i] > maxP) {
			maxP = logits[i];
			maxIdx = i;
		}
	}
	
	rtP = maxP / sum; 
	return labels[maxIdx];
}
//测试
int main()
{
	string xml = "../model/mobilenet_v1_1.0_224_frozen.xml";
	string bin = "../model/mobilenet_v1_1.0_224_frozen.bin";
	string plugin = "../model/plugins.xml";
	string label = "../model/labels.txt";
	string testImg = "../model/test.png";
	initModel(xml, bin, plugin);
	readLabel(label);

	cv::Mat test = cv::imread(testImg);
	cv::Mat rgb;
	cv::cvtColor(test,rgb, cv::COLOR_BGR2RGB);
	float p;
	wstring cls = infer(rgb, p);
	std::wcout.imbue(std::locale("chs"));
	wcout << "类别:" << cls << ",概率:" << p << endl;
}
 

readLabel函数读取label信息,用于将模型识别出的最大概率类别对应的中文文字,测试图片如下:

测试图片

运行后,结果如下:

军用飞机,0.927341

3 附件下载

可以从【附件】中下载所有相关文件,直接用VS2017打开即可,注意只能用x64模式运行,openVNO目前不支持x86。另外,如果CSDN下载没有积分,或者是下载链接出错,可直接加群:824420877,联系群主免费获取代码。

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Python3.X使用Cython调用C/C++

    pxd 文件可以看成是Cython(即pyx文件)的头文件,关于pxd和pyx文件可以简单如下来理解:

    superhua
  • Visual Studio 2017 配置OpenVINO开发环境

    选择windows,登录intel账户后,跳转下载页面,选择Full Package按钮:

    superhua
  • Windows中Python与OpenCV C++之间Mat传递

    将stdafx.h、targetver.h、dllmain.cpp、MyDLL.cpp、stdafxc.pp删除。

    superhua
  • Unity-工具-资源批处理

    对某个文件夹下的Asset进行批处理,设置这些资源的部分属性,这些属性需要通过代码进行设置

    祝你万事顺利
  • 敏捷软件开发学习笔记

    敏捷设计:敏捷设计是一个过程,不是一个事件,它是一个持续的应用原则、模式以及实践来改进软件的结构和可读性的过程,它致力于保持系统设计在任何实践都尽可能得简单,干...

    矿泉水
  • MLSQL拥抱BigDL,轻轻松松无编码玩深度学习

    原谅我,前半句是真的,后半句是噱头,但是真的很简化了。 MLSQL已经有一个相对来比较完善的Python Runtime,细节可以参看这篇文章,所以玩深度学习是...

    用户2936994
  • 敏捷软件开发学习笔记

    敏捷设计:敏捷设计是一个过程,不是一个事件,它是一个持续的应用原则、模式以及实践来改进软件的结构和可读性的过程,它致力于保持系统设计在任何实践都尽可能得简单,干...

    用户2141593
  • 电商系统设计之商品 (中)

    上一篇文章我们讲了关于电商SPU,SKU的概念,以及为何要设计自定义属性与自定义规格并解释了何时可以用到它们。我一直在说电商是一个既简单又复杂的东西,本章我们再...

    CrazyCodes
  • 将你的博客升级为 PWA 渐进式Web离线应用

    PWA 全称 Progressive Web Apps(渐进式 Web 应用程序),旨在使用现有的 Web 技术提供用户更优的使用体验。 基本要求

    ihoey
  • opencv函数之cv.inRange函数

    用户2965768

扫码关注云+社区

领取腾讯云代金券