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

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. rancheros在vm主机部署

    问题描述: 容器化,越来越重要.在云服务中很大比例的服务都跑在容器中,今天介绍rancheros基于容器的os. 特点: 启动快,比较小系统服务也是基于容器化 使用最新的docker release ...

  2. Kubectl Rollout 回滚及Autoscale自动扩容

    Kubectl Rollout 回滚及Autoscale自动扩容 Kubernetes 中采用ReplicaSet(简称RS)来管理Pod.如果当前集群中的Pod实例数少于目标值,RS 会拉起新的Po ...

  3. LOJ#2343. 「JOI 2016 Final」集邮比赛 2

    题目地址 https://loj.ac/problem/2343 题解 首先处理出\(f[i]\)表示以当前位置开头(J,O,I)的合法方案数.这个显然可以\(O(n)\)处理出来.然后考虑在每个位置 ...

  4. 用Queue控制python多线程并发数量

    python多线程如果不进行并发数量控制,在启动线程数量多到一定程度后,会造成线程无法启动的错误. 下面介绍用Queue控制多线程并发数量的方法(python3). # -*- coding: utf ...

  5. Linux——自定义服务命令

    前言 这个写部署禅道的时候包含了这个内容,但是今天弄的时候突然忘记了,所以还是重新写下. 步骤 有的同学可能会不知道一些系统自带的目录是什么意思,所以我这里就拆分下,不直接创建 进入到系统服务目录 c ...

  6. 独角兽估值30亿美金,我们聊聊RPA是什么

    https://www.jianshu.com/p/397ecd238ffc 缩短法定工作时间,已成国际劳动立法趋势,全球政府都曾面对这样的议题,过往企业IT也在思考这件事,开发出更好的软件系统帮助员 ...

  7. Win如何查看某个端口被谁占用并停掉

    第一步在我们的电脑上按win+R键打开运行,输入cmd, 第二步进去命令提示符之后,输入“netstat -ano”,按回车键,查出所有端口,如下图所示: 第三步如果我们想找8089端口,输入nets ...

  8. Can't generate API documentation in l5-swagger

    Can't generate API documentation in l5-swagger Ask Question Asked 3 months ago Active 1 month ago Vi ...

  9. wget递归下载网站资源

    wget -r -p -np -k http://archive.openwrt.org/barrier_breaker/14.07/ramips/mt7620a/packages/ 在下载https ...

  10. 2019.12.09 java for循环

    for(初始化表达式; 循环条件; 操作表达式){     执行语句     ……… } 先走初始化表达式,再走循环条件,如条件满足,走执行语句,然后走操作表达式,再走循环条件,如条件满足,走执行语句 ...