基于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;
}
    这样一段代码,包含了1-8全部8个步骤,可以在具体的情况下被触发(比如按下某个按键)
    需要注意,运行代码之后再正常退出,会报这样一个错误,可能是和系统某种资源没有销毁相关。
2、初步设想:
    但是这样非常低效,所以要提高效率。经过分析研究,最为消耗时间的一步为
 // --------------------------- 4. 读取模型 ------------------------------------------(目视第4步骤最消耗时间)
    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);  //显示原始图像
这个结果,的确是提高了运算的效率。
 
并且能够直接嵌入摄像头循环:
 
//摄像头显示循环,所有关于采集的操作是通过主线程传递控制变量到采集线程,而后由采集线程完成的
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、小结反思。
    得到最后这个结果比较满意,这也是不断尝试的结果。问题产生的原因可能是多方面的,目前也只是得到了基本的解决方法;更系统的方法应该是类化,这个会在后面处理级联问题的时候遇到;而我这里总结出来的函数化的方法,对于解决单模型简单问题,应该已经是可用的了。
    能够看到这里的,一定是遇到了类似问题的同事,那也是对相关问题有较为深入研究的了。
    感谢阅读至此,希望有所帮助。





附件列表

 

posted on 2022-12-03 15:29  jsxyhelu  阅读(22)  评论(0编辑  收藏  举报

导航