[E2E_L9]GOMFCTemplate的融合進階

    在前面出現的融合方法中,最突出的問題就是每次運算,都須要將整個推斷的過程所有操做一遍,這樣確定是費時間的——因此咱們須要將可以獨立的地方獨立出來,可是這個過中很是容易出現溢出的錯誤——通過一段時間的嘗試,終於獲得了相對穩定的結果,這裏將結果記錄下來:
一、原始狀態:
    咱們已經將算法融合到了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個步驟,能夠在具體的狀況下被觸發(好比按下某個按鍵)
    須要注意,運行代碼以後再正常退出,會報這樣一個錯誤,多是和系統某種資源沒有銷燬相關。
二、初步設想:
    可是這樣很是低效,因此要提升效率。通過分析研究,最爲消耗時間的一步爲
 // --------------------------- 4. 讀取模型 ------------------------------------------(目視第4步驟最消耗時間)
    ExecutableNetwork executableNetwork = plugin.LoadNetwork(network, {});
    因此但願可以將這步前調到Oninitdialog中,這樣每次運行過程當中,針對不一樣輸入,只須要運行
    // --------------------------- 7. 推斷結果 -------------------------------------------------
    infer_request.Infer();//多張圖片屢次推斷
    而後將後面的結果進行顯示就能夠了。
三、容易出錯:
    
簡單的想法是直接將executableNetwork變成全局變量,而且在initdialog中申明完成,覺得這樣就可以將耗時的操做解決在 init階段。
可是很是常見的方法是這個。
四、解決方法:
        實際上我沒有專門想出一個什麼方法解決這個問題,我作了較多實驗,不斷去驗證設想,最後發現這個方法可以達到目標。
         將前面的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;
}
     最後的結果是實時效果。那麼這裏的東西是能夠複用的。
五、小結反思。
    獲得最後這個結果比較滿意,這也是不斷嘗試的結果。問題產生的緣由多是多方面的,目前也只是獲得了基本的解決方法;更系統的方法應該是類化,這個會在後面處理級聯問題的時候遇到;而我這裏總結出來的函數化的方法,對於解決單模型簡單問題,應該已是可用的了。
    可以看到這裏的,必定是遇到了相似問題的同事,那也是對相關問題有較爲深刻研究的了。
    感謝閱讀至此,但願有所幫助。




附件列表

相關文章
相關標籤/搜索