前言
在前兩篇文章中我們分別利用已經訓練好的 Caffe 模型來判斷圖像中人物的性別與年紀。而我們之前安裝的 OpenVINO,其實也提供了類似的功能,但是卻只要一個人工智慧網路就可以同時進行性別與年紀的判斷。在這篇文章中,我們就一起來看看如何利用支援 OpenVINO 的 OpenCV 來進行性別與年紀的判斷吧。
在之前的介紹中,我提到通常在進行性別或年紀的判斷前,需要先找出圖像中的臉孔。只是當時為了簡化程式的說明,所以我才直接使用了”大頭照”而省略了找出臉孔與切下臉孔的步驟。在這篇文章中,我們不再忽略這個步驟,將使用內建於 OpenCV 的 Haar Cascade 網路來進行臉孔的辨識。前面我們提到臉孔辨識有很多不同的選擇,而且嚴格說來都可以與性別或年紀辨識的網路任意搭配。然而使用 Haar Cascade 來進行臉孔辨識有一個額外的好處,至於是啥好處,在此就先賣個關子。
接下來,就讓我們開始動手吧。
執行範例程式
在這個 Python 範例程式中,我們僅使用到 OpenCV 這個額外的套件,而且必須是支援 OpenVINO 的特殊版本。
如果你還沒有安裝過 OpenVINO 與 OpenCV,雖然並非絕對必要,但是仍強烈建議在虛擬環境下安裝 OpenCV 與執行範例程式,而樹莓派下的安裝方法可參考之前的文章。在這裡,我沿用之前已經安裝過的樹莓派 Python 虛擬環境來執行此範例程式。不過這個程式不限於樹莓派的環境,包含 Windows 與 macOS 在內的作業系統,只要有安裝支援 OpenVINO 的 OpenCV 即可。
- 進入虛擬環境1workon openvino
- 建立所需目錄1mkdir ~/gender_age_detection; cd ~/gender_age_detection
- 啟用 OpenVINO 環境。1source /opt/intel/openvino/bin/setupvars.sh
- 下載臉孔辨識的模型1wget https://raw.githubusercontent.com/opencv/opencv/master/data/haarcascades/haarcascade_frontalface_default.xml
- 下載訓練過的 IR 模型與網路定義 (For 樹莓派)1wget https://download.01.org/openvinotoolkit/2018_R5/open_model_zoo/age-gender-recognition-retail-0013/FP16/age-gender-recognition-retail-0013.xml1wget https://download.01.org/openvinotoolkit/2018_R5/open_model_zoo/age-gender-recognition-retail-0013/FP16/age-gender-recognition-retail-0013.bin
下載訓練過的 IR 模型與網路定義 (For Windows 與 macOS)1wget https://download.01.org/openvinotoolkit/2018_R5/open_model_zoo/age-gender-recognition-retail-0013/FP32/age-gender-recognition-retail-0013.xml1wget https://download.01.org/openvinotoolkit/2018_R5/open_model_zoo/age-gender-recognition-retail-0013/FP32/age-gender-recognition-retail-0013.bin - 下載範例程式1wget https://raw.githubusercontent.com/cyrilwang/ncs_python_samples/master/openvino_gender_age_detector.py
- 建立圖片目錄1mkdir images
- 下載或自行製作臉孔圖片至 images 目錄下,圖片大小在 250×250 左右。如果你之前已經下載過判斷性別用的圖片,在此可以直接使用。
- 利用下列指令執行範例程式 (使用 NCS/NCS2 神經運算棒)。1python openvino_gender_age_detector.py --xml age-gender-recognition-retail-0013.xml --bin age-gender-recognition-retail-0013.bin --path images --target vpu
或者利用下列指令執行範例程式 (無 NCS/NCS2 神經運算棒)。1python openvino_gender_age_detector.py --xml age-gender-recognition-retail-0013.xml --bin age-gender-recognition-retail-0013.bin --path images
程式執行後我們就可以依序看到各個圖片的判斷結果,如下圖就是其中一幅圖片的結果畫面。依據程式判斷的結果,此為 20 歲的女性面孔:
在我們實際說明程式之前,請再多看上述的圖片幾眼。注意到了嗎?圖片中標示的臉孔是一個正方形的區域,而我們之前利用 OpenVINO IR 模型所抓取的臉孔則是長方形。這可不是湊巧的結果,事實上使用 Haar Cascade 所標出的臉孔區域皆會是正方形,而這就是我之前提到使用 Haar Cascade 的額外好處之一。因為大多數的性別/年紀辨識網路的訓練資料也是使用正方形的圖片,因此當我們將 Haar Cascade 辨識出的臉孔資料送往這些網路時,只會等比例的縮放,而不會產生變形進而影響判斷的結果。不過這個特性在某些情境下可能並不是那麼適當,在使用前須先加以考量。
範例程式說明
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 | # 使用訓練好的 OpenVINO IR 模型來判斷目錄下所有圖片裡人物的性別與年齡 # 載入使用到的套件 import argparse import glob import cv2 # 定義程式執行時的輸入參數 ap = argparse.ArgumentParser() ap.add_argument("-x", "--xml", required=True, help="path to the IR xml file") ap.add_argument("-b", "--bin", required=True, help="path to the IR bin file") ap.add_argument("-p", "--path", help="path to image files") ap.add_argument("-t", "--target", help="which device should be used for inference") args = vars(ap.parse_args()) # 定義模型推論結果 (機率) 所代表的性別,女生 (Female) 在前,男生 (Male) 在後。 GENDERS_FOR_OPENVINO = ['Female', 'Male'] # 根據訓練好的模型建立推論用的 CNN 網路 openvino_net = cv2.dnn.readNet(args["xml"], args["bin"]) # 建立臉孔辨識的網路 face_cascade = cv2.CascadeClassifier('haarcascade_frontalface_default.xml') # 是否使用 NCS/NCS2 進行推論 if args['target'] == 'vpu': openvino_net.setPreferableTarget(cv2.dnn.DNN_TARGET_MYRIAD) else: openvino_net.setPreferableTarget(cv2.dnn.DNN_TARGET_CPU) # 用來判斷性別與年紀並加以標示的函式 def detect_age_and_gender_by_openvino(frame): # 找出圖片中的臉孔 faces = face_cascade.detectMultiScale( frame, scaleFactor=1.2, minNeighbors=6, minSize=(30, 30), flags=cv2.CASCADE_SCALE_IMAGE ) # 針對圖片中的臉孔逐一處理 for (x, y, w, h) in faces: # 將臉孔用綠色框線加以標示 cv2.rectangle(frame, (x, y), (x+w, y+h), (0, 255, 0), 2) # "切下" 臉孔 face = frame[y:y+h, x:x+w] # 將圖片轉換為 np.array,並作為模型的推論輸入 blob = cv2.dnn.blobFromImage(face, size=(62, 62), ddepth=cv2.CV_8U) openvino_net.setInput(blob) # 進行推論 detections = openvino_net.forwardAndRetrieve(['prob', 'age_conv3']) # 取得機率較高推論所代表的性別 gender = GENDERS_FOR_OPENVINO[detections[0][0][0].argmax()] # 計算推論的年紀 age = detections[1][0][0][0][0][0] * 100 # 準備顯示用的文字,包含性別與年紀 text = "gender = {}, age = {:.0f}".format(gender, age) # 根據不同性別採用不同顏色的顯示文字 if gender == 'Male': color = (255, 0, 0) else: color = (0, 0, 255) cv2.putText(frame, text, (10, 20), cv2.FONT_HERSHEY_SIMPLEX, 0.5, color, 1) # 回傳標示過的圖片 return frame # 讀取目錄下的所有檔案 (圖片),取得檔名後讀入圖片、呼叫判斷性別與年紀的函式,並將結果顯示出來 for file in glob.glob(args["path"] + '/*'): filename = str(file) image = cv2.imread(filename) openvino_image = detect_age_and_gender_by_openvino(image) cv2.imshow("OpenVINO", openvino_image) cv2.waitKey(0) |
- 首先我們一樣先載入程式所需的套件 (第 3-5 行),當中載入的 OpenCV (第 5 行) 套件需要支援 OpenVINO。
- 接著程式利用 argparse 套件讀入所需參數 (第 8~13 行):
- xml: IR 模型的網路定義
- bin: IR 模型的訓練結果
- path: 存放推論用圖片的目錄
- target: 是否使用 NCS/NCS2。當指定為 vpu 時,表示使用 NCS/NCS2 進行推論。
- 第 16 行定義模型的性別結果輸出。與之前使用的 Caffe 模型不同,這個模型的輸出為女生機率在前、男生機率在後。此外,這個模型的年紀推論也不是年紀區間,而是一個預測的數值,將此數值 * 100 就是網路所預測的年紀。
- 第 18 行載入網路定義與訓練結果。
- 第 20 行載入臉孔辨識的 Haar Cascade 網路,並指定為 face_cascade。
- 第 22~25 行根據 target 參數的內容決定是否啟用 NCS/NCS2 或單純使用 CPU 進行推論。
- 第 29 行定義函式 detect_age_and_gender_by_openvino,也就是此範例程式的重頭戲。
- 第 31~37 行使用臉孔識別網路 face_cascade 來找出圖片的所有臉孔 (位置)。
- 第 40 行針對所有偵測出的臉孔進行處理。
- 第 42 行使用綠色框線標示出辨識出的臉孔。
- 第 44 行則切下辨識出的臉孔。
- 因為 OpenCV 的 CNN 模組無法直接對圖片原始資訊進行處理,所以一樣透過 blobFromImage (第 46 行) 將圖片陣列轉為合適的格式,而此網路的 Spatial Dimension 則為 (62, 62)。隨後則將轉換過的資料輸入辨識網路 (第 47 行)。
- 第 49 行開始進行預測。之前我們一直使用 forward() 來取得推論結果,但是在這裡卻必須改用 forwardAndRetrieve()。原因在於性別與年紀為兩組不同的推論輸出,所以必須透過 forwardAndRetrieve 並指定輸出的名字,才能夠同時得到兩組的輸出結果。而 prob 與 age_conv3 分別為性別與年紀的輸出名稱,可以在原始文件中找到。如果我們依舊使用 forward() 來取得結果,將只會得到性別的推論。
- 第 51 行利用類似之前的方式取得代表性別的文字。
- 第 53 行計算推論的年紀。前面提到,網路輸出乘上 100 即為推測出的年紀。
- 第 57~61 行根據性別指定不同的文字顏色,並將性別與年紀標示在圖片中。
- 第 63 行回傳標示過的圖片。
- 第 67 行讀取參數 path 目錄下的所有檔案,並逐一加以處理。
- 第 68 行取得檔案名稱。
- 第 69 行載入圖片。
- 第 70 行呼叫年紀辨識的函式 detect_age_and_gender_by_openvino。
- 第 71 行用來顯示標示過的圖片。
- 第 72 行等待使用者按下鍵盤後才繼續執行程式。
推論結果
在此我們針對之前性別判斷的頭像圖片進行性別與年紀推論,其結果整理如下:
在這 20 張圖片中,性別的推論並無產生任何錯誤,效果相當不錯。至於年紀的部分,因為我們無法得知圖像中人物的正確年紀,因此無法做出明確的判斷,不過看起來似乎比之前的結果還要好上一些。
對了,我們在安裝 OpenVINO 時其實也使用過 OpenVINO 內建的臉孔辨識網路,而我們也提到其實可以使用不同的臉孔辨識網路來搭配不同的性別/年紀辨識網路。換句話說,我們可以將上述程式中的 Haar Cascade 網路替換成 OpenVINO 內建的臉孔辨識網路。而下圖就是程式修改後的推論結果,在性別方面的推論一樣沒有失誤,但年紀部分卻有所差距。我們只是改了臉孔辨識的網路,照理說只是影響臉孔判斷的正確性,怎麼會影響到後面網路對於年紀的判斷呢?因為不同網路切出來的臉孔大小其實是不一樣的,所以造成輸入到年紀辨識網路的資料也不一樣,自然會產生不一樣的推論結果。
有興趣的朋友可以自行嘗試修改或從這裡下載修改過的程式。

