前提条件
已经安装好 OpenVINO、Visual Studio 2017。
配置项目
-
新建项目 -> C++ Windows 桌面项目 -> Windows 桌面向导 -> 项目命名为 cats_dogs_infer -> 空项目 -> 确定:
-
选中“空项目” -> 确定:
-
右键源文件 -> 新建 cats_dogs_infer.cpp:
-
右键项目 -> 属性 -> 配置选择“所有配置” -> 平台选择“所有平台”:
- 输出目录:
$(SolutionDir)bin\$(Platform)\$(Configuration)\
- 中间目录:
$(SolutionDir)Intermediate\$(Platform)\$(Configuration)\
修改完毕后点击应用。
-
C/C++ -> 常规 -> 平台选中“x64”:
附加包含目录:$(IE_DIR)\include; $(IE_DIR)\src\extension; $(IE_DIR)\samples\common; $(OPENCV_DIR)\include
修改完毕后点击应用。
-
C/C++ -> 预处理器:
预处理器定义中输入:_CRT_SECURE_NO_WARNINGS
-
链接器 -> 常规:
附加库目录:$(IE_DIR)\lib\intel64\$(Configuration); $(OPENCV_DIR)\lib
-
链接器 -> 输入 -> 配置选中“Debug” -> 平台选中“x64”:
附加依赖项中添加:
inference_engined.lib; cpu_extension.lib; opencv_calib3d412d.lib; opencv_core412d.lib; opencv_dnn412d.lib; opencv_features2d412d.lib; opencv_flann412d.lib; opencv_gapi412d.lib; opencv_highgui412d.lib; opencv_imgcodecs412d.lib; opencv_imgproc412d.lib; opencv_ml412d.lib; opencv_objdetect412d.lib; opencv_photo412d.lib; opencv_stitching412d.lib; opencv_video412d.lib; opencv_videoio412d.lib
修改完毕后点击应用。
-
链接器 -> 输入 -> 配置选中“Release”-> 平台选中“x64”:
附加依赖项中添加:inference_engine.lib; cpu_extension.lib; opencv_calib3d412.lib; opencv_core412.lib; opencv_dnn412.lib; opencv_features2d412.lib; opencv_flann412.lib; opencv_gapi412.lib; opencv_highgui412.lib; opencv_imgcodecs412.lib; opencv_imgproc412.lib; opencv_ml412.lib; opencv_objdetect412.lib; opencv_photo412.lib; opencv_stitching412.lib; opencv_video412.lib; opencv_videoio412.lib
其中 cpu_extension.lib 是 CPU 插件扩展库。
- inference_engine.lib 是用于 Release 模式的 OpenVINO 推理引擎库函数,在 C:\Program Files (x86)\IntelSWTools\openvino_2019.3.334\deployment_tools\inference_engine\lib\intel64\Release 目录下。
- inference_engined.lib 是用于 Debug 模式的 OpenVINO 推理引擎库函数,在 C:\Program Files (x86)\IntelSWTools\openvino_2019.3.334\deployment_tools\inference_engine\lib\intel64\Debug 目录下。
- OpenCV 库函数在 C:\Program Files (x86)\IntelSWTools\openvino_2019.3.334\opencv\lib 文件夹中。有带后缀 ‘d’ 的,表示用于 Debug 模式,没有后缀 ‘d’ 的,表示用于 Release 模式。
编写程序
需要修改的地方:
- IR_FileNameNoExt:设置成你电脑上优化后的模型的路径,不要带后缀,如:E:/1-tf_train/workspaces/cats_dogs/optimized_model/ssd_inception_v2_coco(ssd_inception_v2_coco 是模型的名字)
- imageFile:用来测试的图片的路径
//包含必要的头文件
#include<string> //C++ string标准库
#include<inference_engine.hpp> //Inference Engine库
#include<samples/ocv_common.hpp>//OpenCV库及matU8ToBlob函数定义
#include<ext_list.hpp>//IE CPU扩展库
#include<time.h>//C time标准库
using namespace InferenceEngine;
//将OpenCV Mat对象中的图像数据传给为InferenceEngine Blob对象
void frameToBlob(const cv::Mat& frame, InferRequest::Ptr& inferRequest, const std::string& inputName) {
/* 从OpenCV Mat对象中拷贝图像数据到InferenceEngine 输入Blob对象 */
Blob::Ptr frameBlob = inferRequest->GetBlob(inputName);
matU8ToBlob<uint8_t>(frame, frameBlob);
}
//配置推理计算设备,IR文件路径,图片路径,阈值和标签
std::string DEVICE = "CPU";
std::string IR_FileNameNoExt = "E:/1-tf_train/workspaces/cats_dogs/optimized_model/ssd_inception_v2_coco";
std::string imageFile = "E:/1-tf_train/workspaces/cats_dogs/images/train/cat.11.jpg";
float confidence_threshold = 0.7; //取值0~1
std::vector<std::string> labels = { "fake","cat","dog" }; //标签输入
int main(void) {
// ------------------------------------------------------------------------------------------
// --------------------------- 1. 载入硬件插件(Plugin) --------------------------------------
Core ie;
std::cout << "1.Load Plugin..." << std::endl; //输出当前执行步骤信息
std::cout << ie.GetVersions(DEVICE) << std::endl; //输出插件版本信息
/** 载入默认扩展库 **/
if (DEVICE.find("CPU") != std::string::npos) {
/**
* CPU扩展库包含自定义的MKLDNNPlugin层实现
* 这些没有被MKLDNN实现的自定义层对的网络层推理计算很有用
**/
ie.AddExtension(std::make_shared<Extensions::Cpu::CpuExtensions>(), "CPU");
}
// ----------------------------------------------------------------------------------
// ------------------- 2. 读取IR文件 (.xml and .bin files) --------------------------
std::cout << "2.Read IR File..." << std::endl;
CNNNetReader networkReader;
/** 读取神经网络模型文件*.xml **/
networkReader.ReadNetwork(IR_FileNameNoExt + ".xml");
/** 读取神经网络权重文件*.bin **/
networkReader.ReadWeights(IR_FileNameNoExt + ".bin");
/** 获取神经网络对象 **/
CNNNetwork network = networkReader.getNetwork();
// ----------------------------------------------------------------------------------
// -------------------- 3. 配置网络输入输出 -----------------------------------------
std::cout << "3.Prepare Input and Output..." << std::endl;
/** 获得神经网络的输入信息 **/
InputsDataMap inputsInfo(network.getInputsInfo());
std::string imageInputName, imInfoInputName;
InputInfo::Ptr inputInfo = nullptr;
SizeVector inputImageDims;
/** 遍历模型所有的输入blobs **/
for (auto & item : inputsInfo) {
/** 处理保存图像数据的第一个张量 **/
if (item.second->getInputData()->getTensorDesc().getDims().size() == 4) {
imageInputName = item.first;
inputInfo = item.second;
/** 创建第一个输入blob **/
Precision inputPrecision = Precision::U8;
item.second->setPrecision(inputPrecision);
}
else if (item.second->getInputData()->getTensorDesc().getDims().size() == 2) {
imInfoInputName = item.first;
Precision inputPrecision = Precision::FP32;
item.second->setPrecision(inputPrecision);
if ((item.second->getTensorDesc().getDims()[1] != 3 && item.second->getTensorDesc().getDims()[1] != 6)) {
throw std::logic_error("Invalid input info. Should be 3 or 6 values length");
}
}
}
/** 获得神经网络的输出信息 **/
OutputsDataMap outputsInfo(network.getOutputsInfo());
std::string outputName;
DataPtr outputInfo;
for (const auto& out : outputsInfo) {
if (out.second->getCreatorLayer().lock()->type == "DetectionOutput") {
outputName = out.first;
outputInfo = out.second;
}
}
const SizeVector outputDims = outputInfo->getTensorDesc().getDims();
const int maxProposalCount = outputDims[2];
const int objectSize = outputDims[3];
outputInfo->setPrecision(Precision::FP32);
// --------------------------------------------------------------------------------------------
// --------------------------- 4. 载入模型到AI推理计算设备---------------------------------------
std::cout << "4.Load model into device..." << std::endl;
ExecutableNetwork executable_network = ie.LoadNetwork(network, DEVICE, {});
// --------------------------------------------------------------------------------------------
// --------------------------- 5. 创建Infer Request--------------------------------------------
std::cout << "5.Create Infer Request..." << std::endl;
InferRequest::Ptr infer_request = executable_network.CreateInferRequestPtr();
// --------------------------------------------------------------------------------------------
// --------------------------- 6. 准备输入数据 ------------------------------------------------
std::cout << "6.Prepare Input..." << std::endl;
cv::Mat img = cv::imread(imageFile);
frameToBlob(img, infer_request, imageInputName);
const size_t width = (size_t)img.cols;
const size_t height = (size_t)img.rows;
// --------------------------------------------------------------------------------------------
// --------------------------- 7. 执行推理计算 ------------------------------------------------
std::cout << "7.Start inference..." << std::endl;
std::clock_t begin, end;
begin = std::clock();
infer_request->Infer();
end = std::clock();
std::ostringstream infer_time;//计算推理计算所花费的时间
infer_time << "Infer Time:" << (double)(end - begin) << "ms";
cv::putText(img, infer_time.str(), cv::Point2f(0, 12), cv::FONT_HERSHEY_TRIPLEX, 0.6, cv::Scalar(0, 255, 0));
// --------------------------------------------------------------------------------------------
// --------------------------- 8. 处理输出 ----------------------------------------------------
std::cout << "8.Process output blobs..." << std::endl;
const float *detections = infer_request->GetBlob(outputName)->buffer().as<PrecisionTrait<Precision::FP32>::value_type*>();
for (int i = 0; i < maxProposalCount; i++) {
float image_id = detections[i * objectSize + 0];
float confidence = detections[i * objectSize + 2];
auto label = static_cast<int>(detections[i * objectSize + 1]);
float xmin = detections[i * objectSize + 3] * width;
float ymin = detections[i * objectSize + 4] * height;
float xmax = detections[i * objectSize + 5] * width;
float ymax = detections[i * objectSize + 6] * height;
if (confidence > confidence_threshold) {
// 仅当> confidence_threshold值时,显示推理计算结果
std::ostringstream conf;
conf << ":" << std::fixed << std::setprecision(3) << confidence;
if (xmin < 0) {
xmin = -xmin;
}
if (ymin < 0) {
ymin = -ymin;
}
if (xmax > width) {
xmax = width;
}
if (ymax > height) {
ymax = height;
}
cv::Point origin;
origin.x = xmin;
origin.y = ymin + 20;
std::cout << label << std::endl;
std::string text = "UNKNOWN";
if (1 <= label && label <= 2) {
text = labels[label];
}
cv::putText(img, text,
origin, cv::FONT_HERSHEY_COMPLEX_SMALL, 1,
cv::Scalar(0, 0, 255));
cv::rectangle(img, cv::Point2f(xmin, ymin), cv::Point2f(xmax, ymax), cv::Scalar(0, 0, 255));
}
}
std::cout << "Infer done!" << std::endl;
cv::imshow("Detection results", img);
cv::waitKey(0);
cv::destroyAllWindows();
return 0;
}
编译执行
完成 cats_dogs_infer.cpp 代码后,将解决方案配置设置为 Release 和 x64,然后点击本地 Windows 调试器,编译并运行 cats_dogs_infer 程序。
切换 AI 推理计算硬件
英特尔所有的 AI 计算硬件都使用统一的 OpenVINO 工具套件进行应用程序开发,在切换硬件的时候,不需要修改一行代码,只需更改插件名称,即可完成 AI 推理硬件的切换。