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





附件列表

原文地址:https://www.cnblogs.com/jsxyhelu/p/11340822.html