在前面出现的融合方法中,最突出的问题就是每次运算,都需要将整个推断的过程全部操作一遍,这样肯定是费时间的——所以我们需要将能够独立的地方独立出来,但是这个过中非常容易出现溢出的错误——经过一段时间的尝试,终于得到了相对稳定的结果,这里将结果记录下来:

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、小结反思。
    得到最后这个结果比较满意,这也是不断尝试的结果。问题产生的原因可能是多方面的,目前也只是得到了基本的解决方法;更系统的方法应该是类化,这个会在后面处理级联问题的时候遇到;而我这里总结出来的函数化的方法,对于解决单模型简单问题,应该已经是可用的了。
    能够看到这里的,一定是遇到了类似问题的同事,那也是对相关问题有较为深入研究的了。
    感谢阅读至此,希望有所帮助。

附件列表

[E2E_L9]GOMFCTemplate的融合进阶的更多相关文章

  1. [E2E_L8_1]segmentation_demo道路分割例子和GOMFCTemplate的初步融合

    一.来源 模型例子自己带来副图像     二.简化   #include <algorithm> #include <fstream> #include <iomanip ...

  2. (E2E_L2)GOMfcTemplate在vs2017上的运行并融合Dnn模块

    GOMfcTemplate一直运行在VS2012上运行的,并且开发出来了多个产品.在技术不断发展的过程中,出现了一些新的矛盾:1.由于需要使用DNN模块,而这个模块到了4.0以上的OpenCV才支持的 ...

  3. [BZOI2014]大融合——————线段树进阶

    竟然改了不到一小时就改出来了, 可喜可贺 Description Solution 一开始想的是边两侧简单路径之和的乘积,之后发现这是个树形结构,简单路径数就是节点数. 之后的难点就变成了如何求线段树 ...

  4. C#进阶系列——WebApi 接口参数不再困惑:传参详解

    前言:还记得刚使用WebApi那会儿,被它的传参机制折腾了好久,查阅了半天资料.如今,使用WebApi也有段时间了,今天就记录下API接口传参的一些方式方法,算是一个笔记,也希望能帮初学者少走弯路.本 ...

  5. NLP系列(4)_朴素贝叶斯实战与进阶

    作者: 寒小阳 && 龙心尘 时间:2016年2月. 出处:http://blog.csdn.net/han_xiaoyang/article/details/50629608 htt ...

  6. Ionic APP-Web SPA开发进阶(一)AngularJS全栈工程狮进阶

    AngularJS全栈工程狮进阶 前言 学习了一段时间AngularJS,开始接触移动端APP开发.为了响应公司开发需求,采用"Hybrid"混血开发方法.采用Ionic前端框架, ...

  7. 【进阶3-3期】深度广度解析 call 和 apply 原理、使用场景及实现(转)

    这是我在公众号(高级前端进阶)看到的文章,现在做笔记  https://github.com/yygmind/blog/issues/22 call() 和 apply() call() 方法调用一个 ...

  8. Apollo 1 融合 Spring 的三个入口

    前言 Spring 作为 Java 世界非官方标准框架,任何一个中间件想要得到良好的发展,必须完美支持 Spring 的各种特性,即:无缝融入 Spring. Apollo 作为分布式配置中心,服务于 ...

  9. Scala进阶之路-为什么要学习Scala以及开发环境搭建

    Scala进阶之路-为什么要学习Scala以及开发环境搭建 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 最近人工智能和大数据那是相当的火呀,人工智能带动了Python的流行,区块 ...

随机推荐

  1. 【RAC】 RAC For W2K8R2 安装--共享磁盘的配置(三)

    [RAC] RAC For W2K8R2 安装--共享磁盘的配置(三) 一.1  BLOG文档结构图 一.2  前言部分 一.2.1  导读 各位技术爱好者,看完本文后,你可以掌握如下的技能,也可以学 ...

  2. day 04 预科

    目录 变量 什么是变量 变量的组成 变量名的命名规范 注释 单行注释 多行注释 turtle库的使用 今日内容 数据类型基础 变量 具体的值 存不是目的,取才是目的 为了描述世界万物的状态,因此有了数 ...

  3. H3C WLAN相关组织和标准

  4. less-5

    首先输入id=1和id=1’未报错,均显示You are in.....(如下图所示) 由上图可以看到,如果运行返回结果正确的时候只返回you are in...,不会返回数据库当中的信息了,所以我们 ...

  5. docker postgresql 数据库

    1. 使用docker 镜像 获取镜像:docker pull postgres:9.4 启动: docker run --name postgres1 -e POSTGRES_PASSWORD=pa ...

  6. strtok在keil中使用小笔记及字符串转换为多个浮点数的方法

    在pc上面使用这个字符串函数,是没有问题的,但是我在keil中结合rtos来处理字符串的时候,比如char *s = "1.01313;17.2609;17.4875";那么就只能 ...

  7. Centos7安装Hive2.3

    准备 1.hadoop已部署(若没有可以参考:Centos7安装Hadoop2.7),集群情况如下: hostname IP地址 部署规划 node1 172.20.0.4 NameNode.Data ...

  8. 004——转载—Word2016“此功能看似已中断 并需要修复”问题解决办法

    解决办法如下: 在Win10系统上安装 Office 2016 之后,每次打开Word文档可能都会提示“很抱歉,此功能看似已中断,并需要修复,请使用Windows 控制面板中的“程序和功能”选项修复M ...

  9. C1010 unexpected end of file while looking for precompiled header. Did you forget to add '#include "stdafx.h"' to your source

    提示说是预编译出现问题,提示添加头文件stdafx.h,但是添加了也会继续有其他错误解决方法: 在菜单Project->Properties(或者直接快捷键Alt+F7)->C/C++-& ...

  10. WinDbg命令窗口的使用

    调试器命令窗口是windbg中的主要调试信息窗口.可以在此窗口中输入调试程序命令并查看命令输出.Windbg的命令窗口是我们进行调试时,主要打交道的窗口.界面如下 对于windbg,“调试器命令窗口” ...