前言
在前一篇文章中,我們順利地在樹莓派完成安裝 OpenVINO。在這篇文章中,我們將往下延伸,進入系列文章的重頭戲,也就是安裝 OpenCV,並透過 Python 程式使用 NCS/NCS2 來進行人工智慧的分析。
安裝 Python 虛擬環境
看過我的文章的朋友,應該有印象之前我在樹莓派主要是使用 Python 3.6 的虛擬環境。但是因為 OpenVINO 提供給樹莓派的 OpenCV 僅支援內建的 Python 3.5,所以在這裡我改用 Python 3.5。不過為了管理方便,我們一樣將 OpenCV 安裝在 Python 的虛擬環境裡。
安裝 Pip 與虛擬環境套件
請依序執行下列指令:
1 | cd ~ |
1 | sudo wget https://bootstrap.pypa.io/get-pip.py |
1 | sudo python3 get-pip.py |
1 | sudo pip install virtualenv virtualenvwrapper |
1 | sudo rm -rf get-pip.py ~/.cache/pip |
新增虛擬環境套件所需設定
利用編輯器 (如 vim) 修改 ~/.bashrc
1 | vim ~/.bashrc |
在檔案最後加入
1 2 3 4 | # virtualenv and virtualenvwrapper export VIRTUALENVWRAPPER_PYTHON=/usr/bin/python3 export WORKON_HOME=$HOME/.virtualenvs source /usr/local/bin/virtualenvwrapper.sh |
使用 source 指令使設定生效
1 | source ~/.bashrc |
建立本次所需虛擬環境
利用 mkvirtualenv 指令產生虛擬環境
1 | mkvirtualenv openvino -p python3 |
安裝所需 Python 套件
請依序執行下列指令:
1 | pip install numpy |
1 | pip install picamera |
1 | pip install imutils |
安裝 OpenCV 至虛擬環境
進入虛擬環境的套件目錄
1 | cd ~/.virtualenvs/openvino/lib/python3.5/site-packages/ |
連結 OpenVINO 所包含之 OpenCV 函式庫
1 | ln -s /opt/intel/openvino/python/python3.5/cv2.cpython-35m-arm-linux-gnueabihf.so cv2.so |
執行下列指令確認 OpenCV 已經正確連結
1 | python -c "import cv2;print(cv2.__version__)" |
此時畫面應顯示
1 | 4.1.0-openvino |
由此我們可知,OpenVINO 2019 R1 所提供的是 4.1.0 的特別版 OpenCV。
OpenCV 範例程式一 – 人臉辨識
安裝完 OpenCV 後,我們就先用 Python 程式來實做之前利用 OpenVINO 範例程式所完成的人臉辨識吧。
建立專案目錄
1 | cd ~ |
1 | mkdir openvino |
1 | cd openvino |
1 | workon openvino |
準備所需資源
首先,我們從線上抓取訓練過的模型與參數
1 | wget --no-check-certificate https://download.01.org/opencv/2019/open_model_zoo/R1/models_bin/face-detection-adas-0001/FP16/face-detection-adas-0001.bin |
1 | wget --no-check-certificate https://download.01.org/opencv/2019/open_model_zoo/R1/models_bin/face-detection-adas-0001/FP16/face-detection-adas-0001.xml |
接著取得有人臉的圖片,在此我們使用網路上的免費資源,指令與圖片內容如下:
1 | wget "https://images.pexels.com/photos/1308783/pexels-photo-1308783.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=650&w=940" -O pexels-photo.jpg |
執行範例程式
最後我們抓取 Python 程式
1 | wget https://github.com/cyrilwang/ncs_python_samples/raw/master/openvino_face_detector.py |
執行範例程式。此程式需要圖形化介面功能,所以請在樹莓派本機的 X11 視窗環境或遠端 VNC Server 下執行。
1 | python openvino_face_detector.py --config face-detection-adas-0001.xml --model face-detection-adas-0001.bin --image pexels-photo.jpg |
執行結果應該會顯示出已正確標示 3 張臉孔的圖片,如下圖所示:
範例程式說明
接下來我們就來看看 openvino_face_detector.py 這支 Python 程式做了些甚麼吧。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | # 載入所需函式庫 import argparse import cv2 # 定義程式所需參數 ap = argparse.ArgumentParser() ap.add_argument("-c", "--config", required=True, help="filename of network configuration") ap.add_argument("-m", "--model", required=True, help="filename of trained model") ap.add_argument("-i", "--image", required=True, help="filename of input image") args = vars(ap.parse_args()) # 載入 DNN 模型 net = cv2.dnn.readNet(args["config"], args["model"]) # 定義使用 NCS2 裝置 net.setPreferableTarget(cv2.dnn.DNN_TARGET_MYRIAD) # 載入圖片 frame = cv2.imread(args["image"]) # 將圖片進行轉換後推論 blob = cv2.dnn.blobFromImage(frame, size=(672, 384), ddepth=cv2.CV_8U) net.setInput(blob) out = net.forward() # 標示出偵測到的臉孔 for detection in out.reshape(-1, 7): confidence = float(detection[2]) xmin = int(detection[3] * frame.shape[1]) ymin = int(detection[4] * frame.shape[0]) xmax = int(detection[5] * frame.shape[1]) ymax = int(detection[6] * frame.shape[0]) if confidence > 0.5: cv2.rectangle(frame, (xmin, ymin), (xmax, ymax), color=(0, 255, 0)) # 顯示標示過後的圖片 cv2.imshow("Frame", frame) cv2.waitKey(0) |
- 首先我們載入程式所需的套件 (第 2-3 行),包含 argparse 與 cv2。雖然叫 cv2,其實程式載入的是 OpenCV 4.1.0 喔。
- 接著程式利用 argparse 套件讀入所需參數 (第 6~10 行):
- config: 神經網路的定義
- model: 訓練過後的神經網路模型
- image: 用來識別的圖片路徑與名稱
- 第 13 行載入模型定義與訓練結果。
- 第 16 行指定使用 NCS/NCS2 裝置。第 16 行指定使用 NCS/NCS2 裝置。第 16 行指定使用 NCS/NCS2 裝置。
- 第 19 行載入用來判斷的圖片。
- 因為 OpenCV 的 CNN 模組無法直接對圖片原始資訊進行處理,所以透過 blobFromImage (第 22 行) 將圖片陣列轉為合適的格式。需要注意的是當中的 size=(672, 384) 並非指圖片的尺寸,而是指 CNN 網路的 Spatial Dimension。對 blobFromImage 有興趣深入了解的朋友,可參考 pyimagesearch.com 上的這篇文章。
- 第 23~24 行開始進行預測。
- 第 27~32 行重複對預測出的可能臉孔進行處理。首先取出可靠度 (confidence) 與四個角落的 x, y 座標。當可靠度大於 0.5 時 (也就是 50% 的機率),就用綠色框線加以標示。
- 最後,程式在第 37 行將完成標示的圖片加以顯示,並等待使用者按下鍵盤才結束程式 (第 38 行)。
OpenCV 範例程式二 – 物件辨識
在這個範例中,我們將使用已經訓練過可用來偵測多種物件的 Caffe 模型,並將之應用在判斷影片內容上。
準備所需資源
首先,我們從線上抓取訓練過的模型與參數
1 | wget https://github.com/chuanqi305/MobileNet-SSD/raw/master/mobilenet_iter_73000.caffemodel |
1 | wget https://github.com/chuanqi305/MobileNet-SSD/raw/master/deploy.prototxt |
接著取得有飛機出現的影片,在此我們使用網路上的免費資源,指令與圖片內容如下:
1 | wget https://www.pexels.com/video/1246169/download/ -O pexels-video.mp4 |
執行範例程式
最後我們抓取 Python 程式
1 | wget https://raw.githubusercontent.com/cyrilwang/ncs_python_samples/master/real_time_object_detection.py |
執行範例程式。此程式需要圖形化介面功能,所以請在樹莓派本機的 X11 視窗環境或遠端 VNC Server 下執行。
1 | python real_time_object_detection.py --config deploy.prototxt --model mobilenet_iter_73000.caffemodel --video pexels-video.mp4 |
執行結果應該會開始撥放影片,並標示出影片中的飛機,如下圖所示:
範例程式說明
接下來我們一樣來看看 real_time_object_detection.py 這支 Python 程式做了些甚麼吧。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 | # 載入所需函式庫 from imutils.video import VideoStream import numpy as np import argparse import imutils import time import cv2 # 定義程式所需參數 ap = argparse.ArgumentParser() ap.add_argument("-c", "--config", required=True, help="filename of caffe network configuration") ap.add_argument("-m", "--model", required=True, help="filename of trained caffe model") ap.add_argument("-v", "--video", help="filename of the video (optional)") args = vars(ap.parse_args()) # 如果沒有指定 video 參數就將變數 use_camera 設定為真,表示使用攝影機 use_camera = False if not args.get("video", False): use_camera = True # 定義 MobileNet SSD 訓練過的物件種類 CLASSES = ("background", "aeroplane", "bicycle", "bird", "boat", "bottle", "bus", "car", "cat", "chair", "cow", "diningtable", "dog", "horse", "motorbike", "person", "pottedplant", "sheep", "sofa", "train", "tvmonitor") COLORS = np.random.uniform(0, 255, size=(len(CLASSES), 3)) # 載入 DNN 模型 net = cv2.dnn.readNetFromCaffe(args["config"], args["model"]) # 定義使用 NCS2 裝置 net.setPreferableTarget(cv2.dnn.DNN_TARGET_MYRIAD) # 根據參數決定載入影片或開啟 USB 攝影機 # 如果使用樹莓派的攝影機模組請把 src=0 改為 usePiCamera=True if use_camera: print("開啟攝影機...") vs = VideoStream(src=0).start() time.sleep(2.0) else: vs = cv2.VideoCapture(args["video"]) # 抓取 frame 並加以處理 while True: frame = vs.read() frame = frame if use_camera else frame[1] if frame is None: break frame = imutils.resize(frame, width=400) # 將圖片轉換成 4 維的 blob 陣列 blob = cv2.dnn.blobFromImage(frame, 0.007843, (300, 300), 127.5) # 進行推論 (inference) net.setInput(blob) detections = net.forward() # 處理預測結果 for detection in detections.reshape(-1, 7): index = int(detection[1]) confidence = float(detection[2]) # 如果可能性 > 0.3 則顯示預測結果 if confidence > 0.3: xmin = int(detection[3] * frame.shape[1]) ymin = int(detection[4] * frame.shape[0]) xmax = int(detection[5] * frame.shape[1]) ymax = int(detection[6] * frame.shape[0]) label = "{}: {:.2f}%".format(CLASSES[index], confidence * 100) cv2.rectangle(frame, (xmin, ymin), (xmax, ymax), COLORS[index], 2) y = ymin - 15 if ymin > 30 else ymin + 15 cv2.putText(frame, label, (xmin, ymin -15), cv2.FONT_HERSHEY_SIMPLEX, 0.5, COLORS[index], 2) # 顯示 frame cv2.imshow("Frame", frame) # 如果按下 q 鍵就中斷迴圈 key = cv2.waitKey(1) & 0xFF if key == ord('q'): break # 釋放資源 cv2.destroyAllWindows() if use_camera: vs.stop() else: vs.release() |
- 首先我們一樣先載入程式所需的套件 (第 2-7 行),這次多了 imutils 的相關模組。
- 接著程式利用 argparse 套件讀入所需參數 (第 10~14 行):
- config: 神經網路的定義
- model: 訓練過後的神經網路模型
- video: 用來識別的影片路徑與名稱,如果未指定程式就會從攝影機抓取畫面。
- 第 17~19 行根據是否輸入 video 參數來決定對影片檔進行分析 (use_camera = False) 或是使用攝影機所抓取的畫面 (use_camera = True)。
- 第 22~27 行定義模型所能識別的物件種類,並於第 28 行隨機定義同樣數量的顏色,讓程式可以用不同顏色來標示不同的物件種類。
- 第 31 行載入模型定義與訓練結果。
- 第 34 行指定使用 NCS/NCS2 裝置。第 34 行指定使用 NCS/NCS2 裝置。第 34 行指定使用 NCS/NCS2 裝置。
- 第 38~43 行根據 use_camera 的數值來決定該開啟攝影機或載入影片。指令 VideoStream(src=0).start() 會開啟 (第一台) USB 攝影機,如果您使用的是樹莓派的攝影機模組,須將 src=0 換成 usePiCamera=True。
- 第 46~77 行不斷針對每一個畫面進行判斷,直到我們按下 q 鍵或是影片結尾。
- 第 47~48 行用來讀取畫面。當使用攝影機來抓取畫面時,需要額外的處理才能取得畫面的資料 (第 48 行)。
- 第 49~50 行主要用來判斷是否影片已到盡頭 (或因為特殊原因而沒讀到攝影機的畫面),進而中斷迴圈。
- 第 51 行將畫面縮小為寬度 400 的圖片,以加速處理的速度。
- 因為 OpenCV 的 CNN 模組無法直接對圖片原始資訊進行處理,所以透過 blobFromImage (第 54 行) 將圖片陣列轉為合適的格式。這裡的 (300, 300) 一樣並非指圖片的尺寸,而是指 CNN 網路的 Spatial Dimension。另外兩個參數 0.007843 與 127.5 分別表示 scalefactor 與 mean。其合適的數值與訓練資料有關,而這兩個數字則來自於我們之前下載模型的 github 專案內的範例程式。對 blobFromImage 有興趣深入了解的朋友,可參考 pyimagesearch.com 上的這篇文章。
- 第 57~58 行開始進行預測。
- 第 61~74 行重複對預測出的可能物件進行處理。首先取出可靠度 (confidence) 與四個角落的 x, y 座標。當可靠度大於 0.3 時 (也就是 30% 的機率),就用不同顏色的框線加以標示 (第 72 行)。除了框線之外,也標示出物件的名稱與可靠度 (第 74 行)。
- 第 77 行將標示過的圖片顯示在畫面上。
- 第 80~82 行檢查使用者是否按下 q 鍵,如果是就中斷迴圈。
- 第 85~89 行在程式結束前關閉所有視窗並停止攝影機 (或關閉影片)。
從這兩個範例看來,搭配 NCS/NCS2 來使用 OpenVINO 所特別提供的 OpenCV,與使用一般的 OpenCV 幾乎沒啥差別,就只要多加下列指令即可:
1 | net.setPreferableTarget(cv2.dnn.DNN_TARGET_MYRIAD) |
真可謂是無痛升級 (不過還是會心痛)。此外,副檔名為 xml 與 bin 的檔案也是 OpenVINO 針對推論引擎特別優化過的模型 IR 檔案,因此無法適用於一般版本的 OpenCV,這也是使用時必須注意的地方。
當然,OpenCV 能做的事情遠不止於此,其他部分就待之後有機會再來探索了。


2 comments
請問在Windows下,不用NCS/NCS2,是不是會發生錯誤?
我會發生
out = net.forward()
cv2.error: OpenCV(4.1.1-openvino) C:\jenkins\workspace\OpenCV\OpenVINO\build\opencv\modules\dnn\src\op_inf_engine.cpp:477: error: (-215:Assertion failed) Failed to initiali
ze Inference Engine backend: Unsupported primitive of type: PriorBoxClustered name: fc7_mbox_priorbox in function ‘cv::dnn::InfEngineBackendNet::initPlugin’
你有安裝 NCS/NCS2 嗎?
第 16 行程式有指定使用 NCS/NCS2
net.setPreferableTarget(cv2.dnn.DNN_TARGET_MYRIAD)
如果沒有設備的話,可以改成
net.setPreferableTarget(cv2.dnn.DNN_TARGET_CPU)