2019年11月9日 星期六

OpenVINO與OpenCV搭配─幹電腦視覺的活一點都不累

上個世紀美國科學家發現一個有趣的現象,許多長期在太空執行任務的男性太空人都會產生頭疼、失眠、噁心、情緒低落等症狀,後來發現只要在團隊中加入女性太空人這個現象就會大幅減低,同時增加工作效率,於是就有「男女搭配、幹活不累」的說法產生。同樣的情況,在電腦視覺開源社群領域以往都單靠OpenCV來解決各種影像處理、電腦視覺等問題,近年來隨著「深度學習」技術興起,許多傳統電腦視覺不好處理的問題(如分類、物件偵測、影像分割等)漸漸地也得到不錯的成果。以往這兩大技術都是各自獨立,OpenCV為整合「深度學習」這項技術2017年8月推出3.3版,大幅提升DNN(Depp Neural Network)模組功能[1],加入許多常見框架(如Caffe, TensorFlow, Torch7 …)及常見模型(如AlexNet, VGG, ResNet, SqueezeNet … )造福許多開源社群開發者。2018年Intel推出開源電腦視覺推論及神經網路工具包(OpenVINO)更是直接將OpenCV直接整合進去並給予非常靈活的彈性應用,可各自獨立工作,亦可部份整合或緊密整合,充份體現「OpenVINO與OpenCV搭配,幹電腦視覺的活一點都不累」的精神。

依據OpenVINO的官網教學文件[2],雖有給出OpenVINO和OpenCV各自的範例程式,但對整合測試部份仍沒有太完整的說明。加上之前有一些網友誤會用OpenVINO自帶的OpenCV載入常見框架模型就可以發揮其跨硬體平台(CPU, GPU, FPGA, VPU…)的特性,因此想藉由這篇文章提供大家一些範例程式並幫大家比較一下其中差異及執行效能優劣,希望能讓大家後續使用時能更清楚如何整合應用。
 
Fig. 1 OpenVINO與OpenCV讀取深度學習模型及執行示意圖(OmniXR整理繪製) (點擊圖片放大)



首先介紹如何執行一個常見的深度學習模型,如Fig. 1所示大致有三種作法。
  1. 將常見框架(Caffe, TensorFlow, MxNet, ONNX)產出的模型利用OpenVINO的模型優化器(Model Optimizer, MO)轉換成專用的中介表示檔(Intermediate Representation, IR),即*.xml, *.bin格式檔案,再透過OpenVINO推論引擎(Inference Engin, IE)交由各種不同裝置(如CPU, GPU, VPU, FPGA)進行推論。
  2. 利用OpenCV ReadNetFromModelOptimizer函式(3.4.2以上版本才有支援)直接讀取OpenVINO模型優化器(MO)產出的IR檔案(xml, bin)進行推論工作,執行前需指定後台(Backend)為DNN_BACKEND_INFERENCE_ENGIN(不能設為DNN_BACKEND_DEFAULT或DNN_BACKEND_OPENCV)及執行目標(Target) DNN_TARGET_CPU或DNN_TARGET_GPU或DNN_TARGET_MYRIAD。
  3. 直接將常見框架(Caffe, TensorFlow, Torch7, DarkNet, ONNX)產出的模型直接交由OpenCV專屬函式ReadNetFromXXXX(XXXX表示不同框架)讀取,指定計算後台及目標並執行後續推論工作。若指定後台為IE形式,則目標可像OpenVINO IE一樣執行,可在CPU, GPU, VPU上執行,主要使用Intel MKL-DNN, clDNN, Movidius VPU函式庫進行加速運算工作。若指定後台為預設(DNN_BACKEND_DEFALUT, DNN_BACKEND_OPENCV)則只能使用CPU(SSE, AVX, parallal_for指令集)及GPU(OpenCL, OpenCL_FP16)運行,更多完整說明請參考Fig. 2或[3]

Fig. 2 不同執行後台及目標平台支援項目。(OmniXR整理製作) (點擊圖片放大)

由上表可看出,若直接使用OpenCV時其支援性可能就不如OpenVINO來的完整。若想使用Intel OpenVINO提供優化過的預訓練模型IR檔,那使用OpenCV readNetFromModelOptimizer函式就是最簡易的方式,不過這種方式的執行效能會略低於直接使用OpenVINO。當如果完全不使用OpenVINO情況下也是可透過ReadNetFromXXXX(XXXX表示不同框架)讀取特定框架產生的模型,再利用CPU及GPU進行推論,不過這樣雖然有較大的使用彈性,但卻會失去一些執行效率。

另外OpenVINO只能處理深度學習的推論工作,但推論前的讀取影像(靜態圖檔或動態視頻)或前處理(如調整尺寸、色彩等)及推論後在結果影像上繪製文字、圖框、像素著色等工作還是得仰賴OpenCV的繪圖指令完成。至於推論結果的數據(矩陣資料)處理的工作則常要靠像Numpy或其它矩陣(向量)函式給予協助。

【範例程式說明】


接下來就以Python程式為例,簡單說明三種方式使用上的差異。這裡主要是利用OpenVINO 2019R2版本自帶範例 C:\Program Files (x86)\IntelSWTools\openvino_2019.2.242\inference_engine\samples\python_samples\classification_sample\classification_sample.py [4]做為基礎再進行修改。OpenVINO 這個版本自帶的OpenCV為4.1.1版,而這個範例主要應用的預訓練模型是SqueezeNet Version 1.1版Caffe框架產出的模型,其訓練資料集為ImageNet 1000分類資料集。由於透過OpenVINO Model Downloader下載時只能得到IR檔(*.xml, *.bin)及1000分類標籤檔(*.labels),因此如果要給OpenCV直接使用時還需另外至Github OpenCV Model Zoo Public下載原始Caffe訓練好的SqueezeNet1.1模型(*.prototxt, *.caffemodel)[5]。測試時會提供一張”car.png”的跑車圖檔,輸出結果會產生1000類的機率(或稱置信度),經排序後會顯示機率前五名的分類識別碼(ID),機率值(probability)、標籤說明文字及各階段所花費時間,方便大家比較其中差異。這裡為了說明差異,所只秀出呼叫推論函式最主要的程式碼,而推論結果的顯示部份則省略。

三種不同推論方式完整程式說明及模型、權重、標籤檔請參考OmniXRI Github
(https://github.com/OmniXRI/OpenVINO_OpenCV_Squeezenet)

方式一:classification_openvino.py,直接讀取IR檔(*.xml, *.bin)並使用OpenVINO推論引擎進行推論。使用此方式仍需執行OpenVINO環境變數設定檔 "C:\Program Files (x86)\IntelSWTools\openvino_2019.2.242\bin\setupvars.bat",不然會報錯找不到相關檔案。

model_xml = "squeezenet1.1.xml" #指定IR模型檔(*.xml)
model_bin = "squeezenet1.1.bin" #指定IR權重檔(*.bin)
ie = IECore() #建立推論引擎
net = IENetwork(model=model_xml, weights=model_bin) #載入模型及權重
input_blob = next(iter(net.inputs)) #準備輸入空間
out_blob = next(iter(net.outputs)) #準備輸出空間
net.batch_size = 1 #指定批次讀取數量
n, c, h, w = net.inputs[input_blob].shape #取得批次數量、通道數及影像高、寬
image = cv2.imread("car.png") #指定輸入影像
image = cv2.resize(image, (227, 227)) #統一縮至227x277 for Squeezenet
image = image.transpose((2, 0, 1)) #變更資料格式從 HWC 到 CHW **重要**
exec_net = ie.load_network(network=net, device_name="CPU") #載入模型到指定裝置(CPU, GPU, MYRIAD)並產生工作網路
res = exec_net.infer(inputs={input_blob: image}) #進行推論,輸出結果陣列大小[1,1000,1,1]

方式二:classification_opencv_ir.py,利用OpenCV ReadNetFromModelOptimizer函式直接讀取OpenVINO IR檔案(*.xml, *.bin)進行推論工作。使用此方式也需執行OpenVINO環境變數設定檔 "C:\Program Files (x86)\IntelSWTools\openvino_2019.2.242\bin\setupvars.bat",不然會報錯找不到相關檔案。

model_xml = "squeezenet1.1.xml" #指定IR模型檔(*.xml)
model_bin = "squeezenet1.1.bin" #指定IR權重檔(*.bin)
net = cv2.dnn.readNetFromModelOptimizer(model_xml, model_bin); # 讀取IR檔
net.setPreferableBackend(cv2.dnn.DNN_BACKEND_INFERENCE_ENGINE) #設定後端平台(只能設INFERENCE_ENGINE)
net.setPreferableTarget(cv2.dnn.DNN_TARGET_CPU) #設定目標執行裝置(CPU, OPENCL, OPENCL_FP16, MYRIAD, FPGA)
image = cv2.imread("car.png") # 讀取輸入影像
out_blob = cv2.dnn.blobFromImage( #取得輸出格式[1, 3, 227, 227]
             image, #輸入影像
             scalefactor=1.0, #輸入資料尺度
             size=(227, 227), #輸出影像尺寸
             mean=(0, 0, 0), #從各通道減均值
             swapRB=False, #R、B通道是否交換
             crop=False) #影像是否截切
net.setInput(out_blob) # 設定網路
res = net.forward() #進行推論,輸出結果陣列大小[1,1000,1,1]

方式三:classification_opencv_caffe.py,直接由OpenCV專屬函數ReadNetFromCaffe讀取Caffe訓練好的模型(*.prototxt, *.caffemodel),指定計算後台(BACKEND)及目標(TARGET)即可進行推論工作。這裡的後台可選擇Intel OpenVINO的推論引擎或預設後台,當使用預設後台(DNN_BACKEND_DEFAULT等於DNN_BACKEND_OPENCV)時則只能支援CPU及GPU。若選擇OpenVINO INFERENCE_ENGIN時則目標執行裝置就可包括CPU,GPU,VPU, FPGA,請記得執行OpenVINO環境變數設定檔 "C:\Program Files (x86)\IntelSWTools\openvino_2019.2.242\bin\setupvars.bat",不然會報錯找不到相關檔案。

prototxt = "squeezenet_v1.1.prototxt" #指定Caffe格式參數檔
caffemodel = "squeezenet_v1.1.caffemodel" #指定Caffe格式模型檔
net = cv2.dnn.readNetFromCaffe(prototxt, caffemodel) #載入Caffe模型
net.setPreferableBackend(cv2.dnn.DNN_BACKEND_OPENCV) #設定後端(DNN_BACKEND_DEFAULT, DNN_BACKEND_HALIDE, DNN_BACKEND_INFERENCE_ENGINE, DNN_BACKEND_OPENCV, DNN_BACKEND_VKCOM)
net.setPreferableTarget(cv2.dnn.DNN_TARGET_CPU) #設定目標執行裝置(DNN_TARGET_CPU, DNN_TARGET_OPENCL, DNN_TARGET_OPENCL_FP16, DNN_TARGET_MYRIAD, DNN_TARGET_VULKAN, DNN_TARGET_FPGA)
image = cv2.imread("car.png") # 讀取輸入影像
out_blob = cv2.dnn.blobFromImage( #取得輸出格式 [1, 3, 227, 227]
             image, # 輸入影像
             scalefactor=1.0, # 輸入資料尺度
             size=(227, 227), # 輸出影像尺寸
             mean=(0, 0, 0), # 從各通道減均值
             swapRB=False, # R、B通道是否交換
             crop=False) # 影像是否截切
net.setInput(out_blob) # 設定網路
res = net.forward() #進行推論,輸出結果陣列大小[1,1000,1,1]

【實驗結果說明】


依照上面範例程式,分別進行測試。

如Fig. 3所示,是使用OpenVINO讀取IR檔,在不同裝置下執行結果。從Fig. 3可看出,影像辨識能力(Top5 ID及機率)幾乎沒有差異,而整體執行時間是CPU比較快,但單純就推論來看則是GPU較快,而其最大差別就是模型載入時間。若只讀一張圖測試,那GPU推論整體執行時間就沒有優勢,但若大量執行或是視頻時,那模型載入時間就可忽略不計。

Fig. 3 使用OpenVINO直接讀取IR檔在不同裝置執行結果。(OmniXR整理製作) (點擊圖片放大)

如Fig. 4所示,是使用OpenCV配合readNetFromModleOptimizer函式直接讀取OpenVINO IR檔並在不同裝置上執行結果。同樣地,影像辨識能力(Top5 ID及機率)幾乎沒有差異,但這裡出現一個奇怪的現象,是GPU的推論時間明顯變高很多,有些不合理,至於是什麼原因造成,可能需要更多實驗來分析,這裡暫不下結論。

Fig. 4 使用OpenCV直接讀取IR在不同裝置執行結果。(OmniXR整理製作) (點擊圖片放大)

如Fig. 5所示,則是使用Caffe產出的Squeezenet模型在不同後台(OpenVINO, OpenCV)相同裝置(CPU)下執行結果。由數據可看出,影像辨識能力(Top5 ID及機率)幾乎沒有差異,若從整體時間表現來看,OpenCV直接讀取Caffe模型執行推論效率最好,但只從推論表現來看則是OpenVINO最快,其中最主要的差異就是讀取模型時間。

Fig. 5  不同方式執行Caffe產出之Squeezenet模型結果。(OmniXR整理製作) (點擊圖片放大)

最後以OpenCV直接讀取Caffe模型並以不同裝置(CPU, GPU)進行推論,如Fig. 5所示,影像辨識能力(Top5 ID及機率)幾乎沒有差異,但這裡同樣發生GPU計算耗時比CPU多出許多問題,有待更進一步研究造成原因。

Fig.6  OpenCV直接讀取Caffe模型在不同裝置執行結果。(OmniXR整理製作) (點擊圖片放大)

小結


OpenCV以往是電腦視覺中最不可或缺的開源工具,如今新增了OpenVINO這項開源工具更能強化「深度學習」技術在電腦視覺的相關應用,只要好好搭配OpenCV和OpenVINO使用,從此電腦視覺的活就怎麼幹都不累了。

參考文獻:

[1] OpenCV DNN模組線上說明文件(version 4.1.1)
https://docs.opencv.org/4.1.1/d6/d0f/group__dnn.html

[2] OpenVINO線上說明文件(version 2019_R2)
https://docs.openvinotoolkit.org/2019_R2/index.html

[3] OpenCV之DNN模塊,實現深度學習網絡的推理加速
https://www.twblogs.net/a/5d29a1c2bd9eee1ede072a0c

[4] OpenVINO Toolkit Image Classification Python Sample
https://docs.openvinotoolkit.org/latest/_inference_engine_ie_bridges_python_sample_classification_sample_README.html

[5] Github OpenCV Open Model Zoo Models Public SqueezeNet version 1.1
https://github.com/opencv/open_model_zoo/tree/master/models/public/squeezenet1.1

沒有留言:

張貼留言

【頂置】簡報、源碼、系列文快速連結區

常有人反應用手機瀏覽本部落格時常要捲很多頁才能找到系列發文、開源專案、課程及活動簡報,為了方便大家快速查詢,特整理連結如下,敬請參考! Edge AI Taiwan 邊緣智能交流區 全像顯示與互動交流區 台科大(NTUST) 人工智慧與邊緣運算實務 開南大學...