基于OpenVINO的端到端DL网络--GOMFCTemplate的融合进阶
在前面出现的融合方法中,最突出的问题就是每次运算,都需要将整个推断的过程全部操作一遍,这样肯定是费时间的——所以我们需要将能够独立的地方独立出来,但是这个过中非常容易出现溢出的错误——经过一段时间的尝试,终于得到了相对稳定的结果,这里将结果记录下来:
1、原始状态:
我们已经将算法融合到了MFC中,并且能够发挥作用:
// 用于推断的函数
Mat CGOMfcTemplate2Dlg : :IEInfer(Mat m_mainframe)
{
//初始化IE
// --------------------------- 1.为IE准备插件-------------------------------------
InferencePlugin plugin(PluginDispatcher().getSuitablePlugin(TargetDevice : :eCPU));
plugin.AddExtension(std : :make_shared <Extensions : :Cpu : :CpuExtensions >()); //Extension,useful
// --------------------------- 2.读取IR模型(xml和bin)---------------------------------
CNNNetReader networkReader;
networkReader.ReadNetwork( "./road-segmentation-adas-0001.xml");
networkReader.ReadWeights( "./road-segmentation-adas-0001.bin");
CNNNetwork network = networkReader.getNetwork();
// --------------------------- 3. 准备输入输出的------------------------------------------
InputsDataMap inputInfo(network.getInputsInfo()); //获得输入信息
if (inputInfo.size() != 1) throw std : :logic_error( "错误,该模型应该为单输入");
auto lrInputInfoItem = inputInfo[ "data"]; //开始读入
int w = static_cast < int >(lrInputInfoItem - >getTensorDesc().getDims()[ 3]); //模型要求的输入大小
int h = static_cast < int >(lrInputInfoItem - >getTensorDesc().getDims()[ 2]);
network.setBatchSize( 1); //只有1副图片,故BatchSize = 1
//准备输出数据
OutputsDataMap outputInfo(network.getOutputsInfo()); //获得输出信息
std : :string firstOutputName;
for ( auto &item : outputInfo) {
if (firstOutputName.empty()) {
firstOutputName = item.first;
}
DataPtr outputData = item.second;
if ( !outputData) {
throw std : :logic_error( "错误的格式,请检查!");
}
item.second - >setPrecision(Precision : :FP32);
}
// --------------------------- 4. 读取模型 ------------------------------------------(目视第4步骤最消耗时间)
ExecutableNetwork executableNetwork = plugin.LoadNetwork(network, {});
// --------------------------- 5. 创建推断 -------------------------------------------------
infer_request = executableNetwork.CreateInferRequest();
// --------------------------- 6. 将数据塞入模型 -------------------------------------------------
Blob : :Ptr lrInputBlob = infer_request.GetBlob( "data"); //data这个名字是我看出来的,实际上这里可以更统一一些
matU8ToBlob <float_t >(m_mainframe, lrInputBlob, 0); //重要的转换函数,第3个参数是batchSize,应该是自己+1的
// --------------------------- 7. 推断结果 -------------------------------------------------
infer_request.Infer(); //多张图片多次推断
// --------------------------- 8. 处理结果-------------------------------------------------------
const Blob : :Ptr outputBlob = infer_request.GetBlob(firstOutputName);
const auto outputData = outputBlob - >buffer().as <PrecisionTrait <Precision : :FP32 > : :value_type * >();
size_t numOfImages = outputBlob - >getTensorDesc().getDims()[ 0];
size_t numOfChannels = outputBlob - >getTensorDesc().getDims()[ 1];
h = outputBlob - >getTensorDesc().getDims()[ 2];
w = outputBlob - >getTensorDesc().getDims()[ 3];
size_t nunOfPixels = w * h; //写在内存里的结果,还是要拼出来的
std : :vector <cv : :Mat > imgPlanes{ cv : :Mat(h, w, CV_32FC1, &(outputData[ 0])),
cv : :Mat(h, w, CV_32FC1, &(outputData[nunOfPixels])),
cv : :Mat(h, w, CV_32FC1, &(outputData[nunOfPixels * 2])) };
for ( auto & img : imgPlanes) //本来是平的
img.convertTo(img, CV_8UC1, 255);
cv : :Mat resultImg;
cv : :merge(imgPlanes, resultImg);
return resultImg;
}
Mat CGOMfcTemplate2Dlg : :IEInfer(Mat m_mainframe)
{
//初始化IE
// --------------------------- 1.为IE准备插件-------------------------------------
InferencePlugin plugin(PluginDispatcher().getSuitablePlugin(TargetDevice : :eCPU));
plugin.AddExtension(std : :make_shared <Extensions : :Cpu : :CpuExtensions >()); //Extension,useful
// --------------------------- 2.读取IR模型(xml和bin)---------------------------------
CNNNetReader networkReader;
networkReader.ReadNetwork( "./road-segmentation-adas-0001.xml");
networkReader.ReadWeights( "./road-segmentation-adas-0001.bin");
CNNNetwork network = networkReader.getNetwork();
// --------------------------- 3. 准备输入输出的------------------------------------------
InputsDataMap inputInfo(network.getInputsInfo()); //获得输入信息
if (inputInfo.size() != 1) throw std : :logic_error( "错误,该模型应该为单输入");
auto lrInputInfoItem = inputInfo[ "data"]; //开始读入
int w = static_cast < int >(lrInputInfoItem - >getTensorDesc().getDims()[ 3]); //模型要求的输入大小
int h = static_cast < int >(lrInputInfoItem - >getTensorDesc().getDims()[ 2]);
network.setBatchSize( 1); //只有1副图片,故BatchSize = 1
//准备输出数据
OutputsDataMap outputInfo(network.getOutputsInfo()); //获得输出信息
std : :string firstOutputName;
for ( auto &item : outputInfo) {
if (firstOutputName.empty()) {
firstOutputName = item.first;
}
DataPtr outputData = item.second;
if ( !outputData) {
throw std : :logic_error( "错误的格式,请检查!");
}
item.second - >setPrecision(Precision : :FP32);
}
// --------------------------- 4. 读取模型 ------------------------------------------(目视第4步骤最消耗时间)
ExecutableNetwork executableNetwork = plugin.LoadNetwork(network, {});
// --------------------------- 5. 创建推断 -------------------------------------------------
infer_request = executableNetwork.CreateInferRequest();
// --------------------------- 6. 将数据塞入模型 -------------------------------------------------
Blob : :Ptr lrInputBlob = infer_request.GetBlob( "data"); //data这个名字是我看出来的,实际上这里可以更统一一些
matU8ToBlob <float_t >(m_mainframe, lrInputBlob, 0); //重要的转换函数,第3个参数是batchSize,应该是自己+1的
// --------------------------- 7. 推断结果 -------------------------------------------------
infer_request.Infer(); //多张图片多次推断
// --------------------------- 8. 处理结果-------------------------------------------------------
const Blob : :Ptr outputBlob = infer_request.GetBlob(firstOutputName);
const auto outputData = outputBlob - >buffer().as <PrecisionTrait <Precision : :FP32 > : :value_type * >();
size_t numOfImages = outputBlob - >getTensorDesc().getDims()[ 0];
size_t numOfChannels = outputBlob - >getTensorDesc().getDims()[ 1];
h = outputBlob - >getTensorDesc().getDims()[ 2];
w = outputBlob - >getTensorDesc().getDims()[ 3];
size_t nunOfPixels = w * h; //写在内存里的结果,还是要拼出来的
std : :vector <cv : :Mat > imgPlanes{ cv : :Mat(h, w, CV_32FC1, &(outputData[ 0])),
cv : :Mat(h, w, CV_32FC1, &(outputData[nunOfPixels])),
cv : :Mat(h, w, CV_32FC1, &(outputData[nunOfPixels * 2])) };
for ( auto & img : imgPlanes) //本来是平的
img.convertTo(img, CV_8UC1, 255);
cv : :Mat resultImg;
cv : :merge(imgPlanes, resultImg);
return resultImg;
}
这样一段代码,包含了1-8全部8个步骤,可以在具体的情况下被触发(比如按下某个按键)
需要注意,运行代码之后再正常退出,会报这样一个错误,可能是和系统某种资源没有销毁相关。
2、初步设想:
但是这样非常低效,所以要提高效率。经过分析研究,最为消耗时间的一步为
// --------------------------- 4. 读取模型 ------------------------------------------(目视第4步骤最消耗时间)
ExecutableNetwork executableNetwork = plugin.LoadNetwork(network, {});
ExecutableNetwork executableNetwork = plugin.LoadNetwork(network, {});
所以希望能够将这步前调到Oninitdialog中,这样每次运行过程中,针对不同输入,只需要运行
// --------------------------- 7. 推断结果 -------------------------------------------------
infer_request.Infer();//多张图片多次推断
然后将后面的结果进行显示就可以了。
3、容易出错:
简单的想法是直接将executableNetwork变成全局变量,并且在initdialog中申明完成,以为这样就能够将耗时的操作解决在
init阶段。
但是非常常见的方法是这个。
4、解决方法:
实际上我没有专门想出一个什么方法解决这个问题,我做了较多实验,不断去验证设想,最后发现这个方法能够达到目标。
将前面的1-4步分解为以下函数(通过这个过程,发现原来代码里面一些不需要的部分):
CNNNetwork CGOMfcTemplate2Dlg : :IENetWork(string strXML, string strBIN)
{
CNNNetReader networkReader;
networkReader.ReadNetwork(strXML);
networkReader.ReadWeights(strBIN);
CNNNetwork network = networkReader.getNetwork();
return network;
}
string CGOMfcTemplate2Dlg : :IENetSetup(CNNNetwork network)
{
InputsDataMap inputInfo(network.getInputsInfo()); //获得输入信息
BlobMap inputBlobs; //保持所有输入的blob数据
if (inputInfo.size() != 1) throw std : :logic_error( "错误,该模型应该为单输入");
auto lrInputInfoItem = inputInfo[ "data"]; //开始读入
int h = static_cast < int >(lrInputInfoItem - >getTensorDesc().getDims()[ 2]);
int w = static_cast < int >(lrInputInfoItem - >getTensorDesc().getDims()[ 3]); //模型要求的输入大小
network.setBatchSize( 1); //只有1副图片,故BatchSize = 1
//准备输出数据
OutputsDataMap outputInfo(network.getOutputsInfo()); //获得输出信息
std : :string firstOutputName;
for ( auto &item : outputInfo) {
if (firstOutputName.empty()) {
firstOutputName = item.first;
}
DataPtr outputData = item.second;
if ( !outputData) {
throw std : :logic_error( "错误的格式,请检查!");
}
item.second - >setPrecision(Precision : :FP32);
}
return firstOutputName;
}
InferencePlugin CGOMfcTemplate2Dlg : :IEplugin(CNNNetwork network)
{
InferencePlugin plugin(PluginDispatcher().getSuitablePlugin(TargetDevice : :eCPU));
plugin.AddExtension(std : :make_shared <Extensions : :Cpu : :CpuExtensions >()); //Extension,useful
return plugin;
}
ExecutableNetwork CGOMfcTemplate2Dlg : :getNetWork(InferencePlugin plugin, CNNNetwork network)
{
ExecutableNetwork executableNetwork = plugin.LoadNetwork(network, {});
return executableNetwork;
}
而后分别在oninitdialog和业务代码中这样执行
std
:
:string firstOutputName
= IENetSetup(network);
InferRequest infer_request = executableNetwork.CreateInferRequest();
Blob : :Ptr lrInputBlob = infer_request.GetBlob( "data");
matU8ToBlob <float_t >(m_mainframe, lrInputBlob, 0); //重要的转换函数,第3个参数是batchSize,应该是自己+1的
// ---------------------------推断结果 -------------------------------------------------
infer_request.Infer(); //多张图片多次推断
// ---------------------------处理结果-------------------------------------------------------
const Blob : :Ptr outputBlob = infer_request.GetBlob(firstOutputName);
const auto outputData = outputBlob - >buffer().as <PrecisionTrait <Precision : :FP32 > : :value_type * >();
size_t numOfImages = outputBlob - >getTensorDesc().getDims()[ 0];
size_t numOfChannels = outputBlob - >getTensorDesc().getDims()[ 1];
int h = outputBlob - >getTensorDesc().getDims()[ 2];
int w = outputBlob - >getTensorDesc().getDims()[ 3];
size_t nunOfPixels = w * h; //写在内存里的结果,还是要拼出来的
std : :vector <cv : :Mat > imgPlanes{ cv : :Mat(h, w, CV_32FC1, &(outputData[ 0])),
cv : :Mat(h, w, CV_32FC1, &(outputData[nunOfPixels])),
cv : :Mat(h, w, CV_32FC1, &(outputData[nunOfPixels * 2])) };
for ( auto & img : imgPlanes) //本来是平的
img.convertTo(img, CV_8UC1, 255);
cv : :Mat resultImg;
cv : :merge(imgPlanes, resultImg);
showImage(resultImg, IDC_PIC); //显示原始图像
InferRequest infer_request = executableNetwork.CreateInferRequest();
Blob : :Ptr lrInputBlob = infer_request.GetBlob( "data");
matU8ToBlob <float_t >(m_mainframe, lrInputBlob, 0); //重要的转换函数,第3个参数是batchSize,应该是自己+1的
// ---------------------------推断结果 -------------------------------------------------
infer_request.Infer(); //多张图片多次推断
// ---------------------------处理结果-------------------------------------------------------
const Blob : :Ptr outputBlob = infer_request.GetBlob(firstOutputName);
const auto outputData = outputBlob - >buffer().as <PrecisionTrait <Precision : :FP32 > : :value_type * >();
size_t numOfImages = outputBlob - >getTensorDesc().getDims()[ 0];
size_t numOfChannels = outputBlob - >getTensorDesc().getDims()[ 1];
int h = outputBlob - >getTensorDesc().getDims()[ 2];
int w = outputBlob - >getTensorDesc().getDims()[ 3];
size_t nunOfPixels = w * h; //写在内存里的结果,还是要拼出来的
std : :vector <cv : :Mat > imgPlanes{ cv : :Mat(h, w, CV_32FC1, &(outputData[ 0])),
cv : :Mat(h, w, CV_32FC1, &(outputData[nunOfPixels])),
cv : :Mat(h, w, CV_32FC1, &(outputData[nunOfPixels * 2])) };
for ( auto & img : imgPlanes) //本来是平的
img.convertTo(img, CV_8UC1, 255);
cv : :Mat resultImg;
cv : :merge(imgPlanes, resultImg);
showImage(resultImg, IDC_PIC); //显示原始图像
这个结果,的确是提高了运算的效率。
并且能够直接嵌入摄像头循环:
//摄像头显示循环,所有关于采集的操作是通过主线程传递控制变量到采集线程,而后由采集线程完成的
DWORD WINAPI CaptureThread(LPVOID lpParameter)
{
CGOMfcTemplate2Dlg * pDlg = (CGOMfcTemplate2Dlg *)lpParameter;
double t_start = ( double)cv : :getTickCount(); //开始时间
Mat tmpPrydown;
//#pragma omp parallel for
while ( true)
{
if (pDlg - >b_closeCam) //退出循环
break;
double t = (( double)cv : :getTickCount() - t_start) / getTickFrequency();
if (t < = 0. 1) //fps =10,主动降低速度
{
Sleep( 100);
continue;
}
else
{
t_start = ( double)cv : :getTickCount();
}
//从directX中获得当前图像并显示出来
IplImage * queryframe = pDlg - >cameraDs.QueryFrame();
//在2.0版本中可以强转,在3.0中需要使用函数
Mat camframe = cvarrToMat(queryframe);
pDlg - >showImage(camframe, IDC_CAM); //显示原始图像
根据条件,决定是否采用算法
Mat dst;
Mat img;
Mat tmp;
Mat divideGaussMin;
Mat divideGaussMiddle;
Mat divideGaussMax;
cvtColor(camframe, img, COLOR_BGR2GRAY);
cvtColor(img, img, COLOR_GRAY2BGR);
if (pDlg - >bMethod) //这里实现的是灰度转彩色
{
//算法
if (img.empty())
{
return - 1;
}
std : :string firstOutputName = pDlg - >IENetSetup(pDlg - >network);
InferRequest infer_request = pDlg - >executableNetwork.CreateInferRequest();
Blob : :Ptr lrInputBlob = infer_request.GetBlob( "data");
matU8ToBlob <float_t >(img, lrInputBlob, 0); //重要的转换函数,第3个参数是batchSize,应该是自己+1的
// ---------------------------推断结果 -------------------------------------------------
infer_request.Infer(); //多张图片多次推断
// ---------------------------处理结果-------------------------------------------------------
const Blob : :Ptr outputBlob = infer_request.GetBlob(firstOutputName);
const auto outputData = outputBlob - >buffer().as <PrecisionTrait <Precision : :FP32 > : :value_type * >();
size_t numOfImages = outputBlob - >getTensorDesc().getDims()[ 0];
size_t numOfChannels = outputBlob - >getTensorDesc().getDims()[ 1];
int h = outputBlob - >getTensorDesc().getDims()[ 2];
int w = outputBlob - >getTensorDesc().getDims()[ 3];
size_t nunOfPixels = w * h; //写在内存里的结果,还是要拼出来的
std : :vector <cv : :Mat > imgPlanes{ cv : :Mat(h, w, CV_32FC1, &(outputData[ 0])),
cv : :Mat(h, w, CV_32FC1, &(outputData[nunOfPixels])),
cv : :Mat(h, w, CV_32FC1, &(outputData[nunOfPixels * 2])) };
for ( auto & img : imgPlanes) //本来是平的
img.convertTo(img, CV_8UC1, 255);
cv : :merge(imgPlanes, dst);
}
else
{
dst = img.clone();
}
pDlg - >showImage(dst, IDC_PIC); //显示网络处理图像
}
return 0;
}
DWORD WINAPI CaptureThread(LPVOID lpParameter)
{
CGOMfcTemplate2Dlg * pDlg = (CGOMfcTemplate2Dlg *)lpParameter;
double t_start = ( double)cv : :getTickCount(); //开始时间
Mat tmpPrydown;
//#pragma omp parallel for
while ( true)
{
if (pDlg - >b_closeCam) //退出循环
break;
double t = (( double)cv : :getTickCount() - t_start) / getTickFrequency();
if (t < = 0. 1) //fps =10,主动降低速度
{
Sleep( 100);
continue;
}
else
{
t_start = ( double)cv : :getTickCount();
}
//从directX中获得当前图像并显示出来
IplImage * queryframe = pDlg - >cameraDs.QueryFrame();
//在2.0版本中可以强转,在3.0中需要使用函数
Mat camframe = cvarrToMat(queryframe);
pDlg - >showImage(camframe, IDC_CAM); //显示原始图像
根据条件,决定是否采用算法
Mat dst;
Mat img;
Mat tmp;
Mat divideGaussMin;
Mat divideGaussMiddle;
Mat divideGaussMax;
cvtColor(camframe, img, COLOR_BGR2GRAY);
cvtColor(img, img, COLOR_GRAY2BGR);
if (pDlg - >bMethod) //这里实现的是灰度转彩色
{
//算法
if (img.empty())
{
return - 1;
}
std : :string firstOutputName = pDlg - >IENetSetup(pDlg - >network);
InferRequest infer_request = pDlg - >executableNetwork.CreateInferRequest();
Blob : :Ptr lrInputBlob = infer_request.GetBlob( "data");
matU8ToBlob <float_t >(img, lrInputBlob, 0); //重要的转换函数,第3个参数是batchSize,应该是自己+1的
// ---------------------------推断结果 -------------------------------------------------
infer_request.Infer(); //多张图片多次推断
// ---------------------------处理结果-------------------------------------------------------
const Blob : :Ptr outputBlob = infer_request.GetBlob(firstOutputName);
const auto outputData = outputBlob - >buffer().as <PrecisionTrait <Precision : :FP32 > : :value_type * >();
size_t numOfImages = outputBlob - >getTensorDesc().getDims()[ 0];
size_t numOfChannels = outputBlob - >getTensorDesc().getDims()[ 1];
int h = outputBlob - >getTensorDesc().getDims()[ 2];
int w = outputBlob - >getTensorDesc().getDims()[ 3];
size_t nunOfPixels = w * h; //写在内存里的结果,还是要拼出来的
std : :vector <cv : :Mat > imgPlanes{ cv : :Mat(h, w, CV_32FC1, &(outputData[ 0])),
cv : :Mat(h, w, CV_32FC1, &(outputData[nunOfPixels])),
cv : :Mat(h, w, CV_32FC1, &(outputData[nunOfPixels * 2])) };
for ( auto & img : imgPlanes) //本来是平的
img.convertTo(img, CV_8UC1, 255);
cv : :merge(imgPlanes, dst);
}
else
{
dst = img.clone();
}
pDlg - >showImage(dst, IDC_PIC); //显示网络处理图像
}
return 0;
}
最后的结果是实时效果。那么这里的东西是可以复用的。
5、小结反思。
得到最后这个结果比较满意,这也是不断尝试的结果。问题产生的原因可能是多方面的,目前也只是得到了基本的解决方法;更系统的方法应该是类化,这个会在后面处理级联问题的时候遇到;而我这里总结出来的函数化的方法,对于解决单模型简单问题,应该已经是可用的了。
能够看到这里的,一定是遇到了类似问题的同事,那也是对相关问题有较为深入研究的了。
感谢阅读至此,希望有所帮助。
附件列表