新聞中心

        EEPW首頁(yè) > 智能計(jì)算 > 進(jìn)階指南 > 干貨 | 函數(shù)詳解 ?OpenVINO Inference Engine SDK

        干貨 | 函數(shù)詳解 ?OpenVINO Inference Engine SDK

        作者: 時(shí)間:2021-01-20 來(lái)源:OpenVINO中文社區(qū) 收藏

        基本介紹

        本文引用地址:http://www.104case.com/article/202101/422243.htm

        是針對(duì)英特爾針對(duì)自家現(xiàn)有的硬件平臺(tái)開(kāi)發(fā)的高性能計(jì)算機(jī)視覺(jué)和深度學(xué)習(xí)視覺(jué)應(yīng)用的工具套件,支持英特爾自家的 CPU、GPU、FPGA、VPU 等硬件。 包含兩個(gè)大模塊:模型轉(zhuǎn)換模塊 Model Optimizer 和推理模塊 Inference Engine。本文講解推理模塊常見(jiàn)的 C++、API 函數(shù)說(shuō)明以及使用方法,推理模塊 API 也提供 C、Python 接口,筆者安裝的 版本是 2020.3 版本。


        工作流程

        推理模塊的工作流程一般包含如下步驟: 

        1. 創(chuàng)建推理對(duì)象:該推理對(duì)象可以支持不同的設(shè)備,所有的設(shè)備插件自動(dòng) 通過(guò) Core 來(lái)進(jìn)行管理。Core::SetConfig 來(lái)配置設(shè)備屬性,使用 Core::AddExtension 來(lái)注冊(cè)設(shè)備第三方庫(kù),增加自定義層實(shí)現(xiàn)
        2. 讀取中間表示:使用 Core 對(duì)象來(lái)讀取中間表示文件 Core::ReadNetwork 創(chuàng)建 CNNNetwork 對(duì)象,該網(wǎng)絡(luò)存在于宿主機(jī)的內(nèi)存中
        3. 設(shè)置輸入輸出:CNNNetwork::getInputsInfo 和 CNNNetwork::getOutputsInfo 函數(shù)用于設(shè)置輸入輸出層的精度、數(shù)據(jù)排列等
        4. 加載神經(jīng)網(wǎng)絡(luò):CNNNetwork::LoadNetwork 編譯并加載網(wǎng)絡(luò)到設(shè)備, 得到ExecutableNetwork 對(duì)象
        5. 設(shè)置輸入數(shù)據(jù):使用 ExecutableNetwork 對(duì)象來(lái)創(chuàng)建 InferRequest,可以直接將宿主機(jī)的內(nèi)存復(fù)制到設(shè)備內(nèi)存
        6. 執(zhí)行推理過(guò)程:可以選擇同步推理 InferRequest::Infer,也可以選擇異步推理模式 InferRequest::StartAsync
        7. 獲取輸出結(jié)果:InferRequest::GetBlob 讀取推理結(jié)果

        接口詳解

        下面逐一講解上述工作流程中提到的 API 函數(shù),以下函數(shù)聲明來(lái)自于安裝好的 OpenVINO 庫(kù),也可以從 OpenVINO 源碼中理解這些函數(shù)的實(shí)現(xiàn),由于篇幅有限,本文重點(diǎn)講解函數(shù)接口以及使用方法

        創(chuàng)建推理對(duì)象

        Core 類(lèi)是 InferenceEngine 的核心管理類(lèi),負(fù)責(zé)設(shè)備的管理,網(wǎng)絡(luò)加載等功能。頭文件為 openvino/inference_engine/ie_core.hpp,實(shí)現(xiàn)文件在openvino/inference_engine/src/inference_engine/ie_core.cpp。Core 構(gòu)造函數(shù)聲明如下所示

        explicit Core(const std::string& xmlConfigFile = std::string());
        // xmlConfigFile:指定插件配置文件,如果為空的話(huà)加載默認(rèn)配置

        一般創(chuàng)建對(duì)象不輸入插件配置文件,使用默認(rèn)的插件庫(kù),默認(rèn)參數(shù)位于openvino/deployment_tools/inference_engine/lib/intel64/plugins.xml,默認(rèn)參數(shù)如下所示,name 表示支持的設(shè)備類(lèi)型名稱(chēng),location 表示支持設(shè)備對(duì)應(yīng)的庫(kù)名稱(chēng)。

        <ie>
           <plugins>
               <plugin name="GNA" location="libGNAPlugin.so">
               </plugin>
               <plugin name="HETERO" location="libHeteroPlugin.so">
               </plugin>
               <plugin name="CPU" location="libMKLDNNPlugin.so">
               </plugin>
               <plugin name="MULTI" location="libMultiDevicePlugin.so">
               </plugin>
               <plugin name="GPU" location="libclDNNPlugin.so">
               </plugin>
               <plugin name="MYRIAD" location="libmyriadPlugin.so">
               </plugin>
               <plugin name="HDDL" location="libHDDLPlugin.so">
               </plugin>
               <plugin name="FPGA" location="libdliaPlugin.so">
               </plugin>
           </plugins>
        </ie>

        一個(gè)詳細(xì)版本的設(shè)備配置文件如下所示:

        <ie>
           <plugins>
               <plugin name="" location="">
                   <extensions>
                       <extension location="">
                   </extensions>
                   <properties>
                       <property key="" value="">
                   </properties>
               </plugin>
           </plugins>
        </ie>

        以上默認(rèn)的插件庫(kù)除FPGA外均可自行編譯,源碼位于openvino/blob/master/inference-engine/src/inference_engine 內(nèi)。可通過(guò) SetConfig 來(lái)配置設(shè)備的一些屬性也就是上面 xml 中的 property 字段,使用 AddExtension 來(lái)設(shè)置設(shè)備外掛第三方庫(kù)也就是上面 xml 中的 extension 字段,SetConfig 接口函數(shù)如下所示:

        void SetConfig(const std::map<std::string, std::string>& config, const std::string& deviceName = std::string());
        // config:指定配置的參數(shù)名稱(chēng)和數(shù)值
        // deviceName:指定配置設(shè)備名稱(chēng),可選參數(shù),如果不設(shè)置可默認(rèn)為所有注冊(cè)的設(shè)備都更改次配置

        SetConfig 函數(shù)用于為設(shè)備設(shè)置某些屬性值,第一個(gè)參數(shù)為設(shè)備屬性及其值,第二個(gè)參數(shù)為設(shè)備名稱(chēng),如果設(shè)備名稱(chēng)為空,則設(shè)置所有的設(shè)備屬性。設(shè)備屬性查詢(xún)列表可以參考openvino/inference_engine/include/ie_plugin_config.hpp。 
        AddExtension 函數(shù)用于配置設(shè)備的外掛第三方庫(kù),來(lái)支持 OpenVINO 中沒(méi)有實(shí)現(xiàn)的某些層。目前不支持設(shè)備名稱(chēng)為 HETERO 和 MULTI 的外掛第三方庫(kù),可用第二個(gè)函數(shù)接口檢驗(yàn)設(shè)備名稱(chēng)。

        void AddExtension(const IExtensionPtr& extension);
        void AddExtension(IExtensionPtr extension, const std::string& deviceName);
        // extention:已加載的擴(kuò)展的指針
        // deviceName:設(shè)備名稱(chēng)

        創(chuàng)建推理對(duì)象步驟如下示例代碼所示:

        // 使用默認(rèn)的 plugins.xml 文件創(chuàng)建 Core 對(duì)象
        Core ie;

        // 設(shè)置設(shè)備屬性
        ie.SetConfig({{PluginConfigParams::KEY_CONFIG_FILE, config_file}}, device_name);

        // 設(shè)置設(shè)備的外掛第三方庫(kù)用于支持用戶(hù)自定義層
        IExtensionPtr extension_ptr = make_so_pointer<IExtension>(extension_name);
        ie.AddExtension(extension_ptr, "CPU");

        // 設(shè)置設(shè)備的外掛函數(shù)用于支持用戶(hù)自定義層
        IExtensionPtr inPlaceExtension = std::make_shared<InPlaceExtension>();
        ie.AddExtension(inPlaceExtension);

        讀取中間表示

        讀取中間表示的函數(shù)接口為 Core::ReadNetwork 函數(shù),該函數(shù)輸入?yún)?shù)為 Model Optimizer 轉(zhuǎn)換得到中間表示,將其加載到宿主機(jī)內(nèi)存中。函數(shù)聲明如下所示:

        CNNNetwork ReadNetwork(const std::string& modelPath, const std::string& binPath = "") const;
        // modelPath:中間表示的配置文件
        // binPath:中間表示的權(quán)重文件,如果為空,則嘗試加載 modelPath 同名的權(quán)重文件,如果找不到同名文件則不加載權(quán)重
        CNNNetwork ReadNetwork(const std::string& model, const Blob::CPtr& weights) const;
        // model:中間表示的配置文件,權(quán)重文件必須與配置文件同名
        // weights:共享指針,指向常量 Blob

        第二個(gè)函數(shù)不常用,下面僅舉例說(shuō)明第一個(gè)函數(shù)用法,通過(guò) ReadNetwork 函數(shù)創(chuàng)建 CNNNetwork 對(duì)象

        /** Read network model **/
        CNNNetwork network = ie.ReadNetwork(modelPath);

        設(shè)置輸入輸出

        設(shè)置輸入輸出屬性,包括數(shù)據(jù)精度、輸入數(shù)據(jù)的排列方式(NCHW、NHWC等)、BatchSize 等操作。
        排列方式可以從openvino/inference_engine/include/ie_common.h查詢(xún)目前支持的輸入輸出數(shù)據(jù) Layout 方式如下:

        NCHW = 1,  //!< NCHW layout for input / output blobs
        NHWC = 2,  //!< NHWC layout for input / output blobs
        NCDHW = 3,  //!< NCDHW layout for input / output blobs
        NDHWC = 4,  //!< NDHWC layout for input / output blobs

        精度參數(shù)可以從openvino/inference_engine/include/ie_precision.hpp查詢(xún),目前支持的精度參數(shù)方式如下:

        enum ePrecision : uint8_t {
           UNSPECIFIED = 255, /**< Unspecified value. Used by default */
           MIXED = 0,         /**< Mixed value. Can be received from network. No applicable for tensors */
           FP32 = 10,         /**< 32bit floating point value */
           FP16 = 11,         /**< 16bit floating point value */
           Q78 = 20,          /**< 16bit specific signed fixed point precision */
           I16 = 30,          /**< 16bit signed integer value */
           U8 = 40,           /**< 8bit unsigned integer value */
           I8 = 50,           /**< 8bit signed integer value */
           U16 = 60,          /**< 16bit unsigned integer value */
           I32 = 70,          /**< 32bit signed integer value */
           I64 = 72,          /**< 64bit signed integer value */
           U64 = 73,          /**< 64bit unsigned integer value */
           BIN = 71,          /**< 1bit integer value */
           BOOL = 41,         /**< 8bit bool type */
           CUSTOM = 80        /**< custom precision has it's own name and size of elements */
        };

        輸入數(shù)據(jù)的屬性設(shè)置示例代碼如下所示:

        InputsDataMap inputInfo(network.getInputsInfo());
               
        InputInfo::Ptr& input = inputInfo.begin()->second;
        auto inputName = inputInfo.begin()->first;

        // 設(shè)置精度和數(shù)據(jù)排列方式
        input->setPrecision(Precision::U8);
        input->getInputData()->setLayout(Layout::NCHW);

        // 設(shè)置 BatchSize 大小
        ICNNNetwork::InputShapes inputShapes = network.getInputShapes();
        SizeVector& inSizeVector = inputShapes.begin()->second;
        inSizeVector[0] = 1;  // set batch to 1
        network.reshape(inputShapes);

        輸出數(shù)據(jù)的屬性設(shè)置示例代碼如下所示:

        OutputsDataMap outputInfo(network.getOutputsInfo());
        for (auto &output : outputInfo) {
           
        // 設(shè)置精度和數(shù)據(jù)的排列方式
           output.second->setPrecision(Precision::FP32);
           output.second->setLayout(Layout::NCHW);

        }

        加載神經(jīng)網(wǎng)絡(luò)

        加載神經(jīng)網(wǎng)路是將網(wǎng)絡(luò)編譯并加載到設(shè)備上,使用的函數(shù)接口為 Core::LoadNetwork,其函數(shù)聲明如下所示:

        ExecutableNetwork LoadNetwork(
        const CNNNetwork network, const std::string& deviceName,
        const std::map<std::string, std::string>& config = std::map<std::string, std::string>());
        // network:在步驟二讀取中間表示中創(chuàng)建的網(wǎng)絡(luò)
        // deviceName:執(zhí)行推理的設(shè)備名稱(chēng)
        // config:設(shè)備配置屬性,可選參數(shù),該屬性也可以通過(guò) SetConfig 來(lái)設(shè)置所有設(shè)備屬性

        該函數(shù)的用法如下所示:

        ExecutableNetwork executable_network = ie.LoadNetwork(network, device_name, configure);

        LoadNetwork 所執(zhí)行的加載動(dòng)作實(shí)現(xiàn)代碼存在于設(shè)備的 Plugin 插件中,也就是plugins.xml文件中設(shè)置的 so 動(dòng)態(tài)庫(kù),入口函數(shù)為 CreatePluginEngine。

        加載神經(jīng)網(wǎng)絡(luò)

        在執(zhí)行推理之前需要設(shè)置網(wǎng)絡(luò)的輸入數(shù)據(jù),通過(guò) GetBlob 函數(shù)獲取 Blob 指針,然后將輸入數(shù)據(jù)拷貝到設(shè)備內(nèi)存上,在openvino/inference_engine/sample中提供了一種通用的數(shù)據(jù)拷貝方式matU8ToBlob,首先在宿主機(jī)上實(shí)現(xiàn)圖像的縮放,然后將其拷貝到設(shè)備內(nèi)存,其調(diào)用過(guò)程如下:

        // infer_request 在下面`執(zhí)行推理過(guò)程`時(shí)講述
        Blob::Ptr input = infer_request.GetBlob(input_name);
        for (size_t b = 0; b < batch_size; b++) {
           matU8ToBlob<uint8_t>(image, input, b);
        }

        matU8ToBlob 函數(shù)位于openvino/inference_engine/samples/cpp/common/samples/ocv_common.hpp 頭文件內(nèi),其實(shí)現(xiàn)代碼如下所示

        template <typename T>
        void matU8ToBlob(const cv::Mat& orig_image, InferenceEngine::Blob::Ptr& blob, int batchIndex = 0) {
           // orig_image:原始圖像
           // blob:輸入數(shù)據(jù)內(nèi)存
           // batchIndex:批處理的index

           // 首先讀取網(wǎng)路尺寸
           InferenceEngine::SizeVector blobSize = blob->getTensorDesc().getDims();
           const size_t width = blobSize[3];
           const size_t height = blobSize[2];
           const size_t channels = blobSize[1];
           if (static_cast<size_t>(orig_image.channels()) != channels) {
               THROW_IE_EXCEPTION << "The number of channels for net input and image must match";
           }
           T* blob_data = blob->buffer().as<T*>();

           
        // CPU下執(zhí)行原圖的縮放
           cv::Mat resized_image(orig_image);
           if (static_cast<int>(width) != orig_image.size().width ||
                   static_cast<int>(height) != orig_image.size().height) {
               cv::resize(orig_image, resized_image, cv::Size(width, height));
           }

         
         // 獲得內(nèi)存中數(shù)據(jù)偏移位移
           int batchOffset = batchIndex * width * height * channels;

           
        // 完成數(shù)據(jù)從宿主機(jī)到設(shè)備的拷貝過(guò)程,僅支持單通道或者三通道輸入數(shù)據(jù)推理
           if (channels == 1) {
               for (size_t  h = 0; h < height; h++) {
                   for (size_t w = 0; w < width; w++) {
                       blob_data[batchOffset + h * width + w] = resized_image.at<uchar>(h, w);
                   }
               }
           } else if (channels == 3) {
               for (size_t c = 0; c < channels; c++) {
                   for (size_t  h = 0; h < height; h++) {
                       for (size_t w = 0; w < width; w++) {
                           blob_data[batchOffset + c * width * height + h * width + w] =
                                   resized_image.at<cv::Vec3b>(h, w)[c];
                       }
                   }
               }
           } else {
               THROW_IE_EXCEPTION << "Unsupported number of channels";
           }
        }

        Blob 類(lèi)是 OpenVINO 的基礎(chǔ)數(shù)據(jù)單元,其頭文件為openvino/inference_engine/include/ie_blob.h網(wǎng)絡(luò)輸入輸出數(shù)據(jù)的傳遞是通過(guò) Blob 類(lèi)來(lái)實(shí)現(xiàn)的

        執(zhí)行推理過(guò)程

        OpenVINO 提供了兩種模式來(lái)執(zhí)行推理過(guò)程:同步模式下推理函數(shù) Infer 一直會(huì)阻塞直到推理結(jié)束;異步模式下調(diào)用 StartAsync 函數(shù)會(huì)立刻返回,然后再調(diào)用 Wait 函數(shù)等待執(zhí)行結(jié)束,對(duì)于視頻分析或者基于視頻的目標(biāo)檢測(cè)任務(wù),OpenVINO 官方推薦使用異步方式,可以實(shí)現(xiàn)更快幀率的處理,提高推理設(shè)備的利用率,在設(shè)備推斷同時(shí)完成數(shù)據(jù)拷貝過(guò)程,減少推理設(shè)備等待時(shí)間。
        同步模式下函數(shù)的調(diào)用例子如下所示:

        InferRequest infer_request = executable_network.CreateInferRequest();
        infer_request.Infer();
        // 獲取網(wǎng)絡(luò)輸出結(jié)果

        異步模式下必須建立一定的循環(huán)機(jī)制,才可做到正確使用,首先必須創(chuàng)建2個(gè)推理請(qǐng)求,其次是必須結(jié)合使用 StartAsync 和 Wait 函數(shù)來(lái)分別發(fā)送下一幀的推理請(qǐng)求和獲取上一幀的推理結(jié)果。

        InferRequest::Ptr async_infer_request_curr = network.CreateInferRequestPtr();
        InferRequest::Ptr async_infer_request_next = network.CreateInferRequestPtr();

        while (true)
        {
           
        // 設(shè)置下一幀推理數(shù)據(jù)
           frameToBlob(curr_frame, async_infer_request_next, imageInputName);
           // 發(fā)送下一幀推理請(qǐng)求
           async_infer_request_next->StartAsync();
         
        // 獲取上一幀推理結(jié)果
           if (OK == async_infer_request_curr->Wait(IInferRequest::WaitMode::RESULT_READY))
           {
         
        // 獲取網(wǎng)絡(luò)輸出結(jié)果
           }
           
        // 交換兩個(gè)推理指針
           async_infer_request_curr.swap(async_infer_request_next);
        }

        執(zhí)行推理過(guò)程

        獲取輸出結(jié)果需要調(diào)用 GetBlob 函數(shù)獲取輸出數(shù)據(jù)的地址,然后對(duì)其進(jìn)行解析,同步和異步模式下獲取輸出結(jié)果的方法類(lèi)似,下面方法一直接獲取 Blob 指針,然后可以解析 Blob 指針內(nèi)數(shù)據(jù),方法二則直接獲取輸出結(jié)果數(shù)值。

        // 獲取輸出結(jié)果方法一
        Blob::Ptr output_blob = infer_request.GetBlob(output_name);
        MemoryBlob::CPtr moutput = as<MemoryBlob>(infer_request.GetBlob(output_name));

        // 獲取輸出結(jié)果方法二
        const float *detections = async_infer_request_curr->GetBlob(output_name)->buffer().as<PrecisionTrait<Precision::FP32>::value_type*>();

        寫(xiě)在最后

        本文對(duì) OpenVINO 推理模塊常見(jiàn)的 API 接口函數(shù)以及用法進(jìn)行了說(shuō)明,撰寫(xiě)過(guò)程中參考了openvino/inference_engine/sample中例子程序。

        參考鏈接

        1. https://github.com/openvinotoolkit/openvino
        2. https://docs.openvinotoolkit.org/latest/classInferenceEngine_1_1Core.html
        3. https://docs.openvinotoolkit.org/latest/_docs_IE_DG_inference_engine_intro.html
        4. https://docs.openvinotoolkit.org/latest/_docs_IE_DG_Deep_Learning_Inference_Engine_DevGuide.html


        關(guān)鍵詞: OpenVINO

        評(píng)論


        相關(guān)推薦

        技術(shù)專(zhuān)區(qū)

        關(guān)閉
        主站蜘蛛池模板: 灵武市| 镇远县| 来凤县| 佛坪县| 贵阳市| 张家港市| 太谷县| 大渡口区| 探索| 昌黎县| 金溪县| 仙桃市| 望谟县| 油尖旺区| 齐齐哈尔市| 临城县| 日土县| 华阴市| 曲水县| 都江堰市| 林周县| 始兴县| 淮安市| 宝清县| 朝阳县| 阿尔山市| 明溪县| 马公市| 江安县| 宝清县| 柞水县| 洮南市| 富平县| 五寨县| 石门县| 双流县| 阿拉善盟| 苍南县| 滨州市| 丽江市| 乐都县|