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





    附件列表

  • 相关阅读:
    git merge merge错误 —— 纠正
    copy —— docker cp & kubectl cp
    docker —— 获取 仓库中的 tag 列表
    课程——《深度学习的优化方法》
    基础知识篇(干货,次次都有新体悟)——十大经典排序算法2
    基础知识篇(干货,次次都有新体悟)——数据结构
    criteo 接口升级——MAPI deprecated
    CAP
    Redis 数据类型
    十大经典排序算法(转发)
  • 原文地址:https://www.cnblogs.com/jsxyhelu/p/11340822.html
Copyright © 2020-2023  润新知