前言
一連幾篇文章都在討論如何利用 OpenCV 來進行性別與年紀的判斷,也使用了兩種不同的方式 (Haar Cascade 網路與 OpenVINO 內建網路) 來標示出圖片中的臉孔。不過之前都是針對靜態的圖片,而這次我們則將目標放在動態的影片以及攝影畫面上。在這篇文章中,我將介紹如何在樹莓派下利用 OpenCV 即時針對影片內容進行臉孔偵測,並做出性別與年紀的推論。在性別與年紀推論方面,我們一樣使用 OpenVINO 內建的辨識網路。至於臉孔偵測的部分,我們改用第三種作法,那就是 OpenCV 內建的 Caffe 網路。這個範例最好使用 Intel 神經運算棒 NCS/NCS2 來提升推論的速度,以達到即時的效果。
影像與圖片
雖然影片有很多種格式及壓縮方式,但是基本上為了播放的緣故,最後都是以一個畫面接著一個畫面的方式加以輸出。也就是說其實我們可以把影片看成是一堆連續的圖片,直接應用我們之前處理靜態圖片的方式來進行性別與年紀的辨識。這樣做雖然無法充分利用影像連續的好處,甚至會出現奇怪的判斷 (例如同一張臉孔忽男忽女),不過因為處理起來相當簡單,所以在一般情況下仍是相當不錯的選擇。
除了事先錄製好的影片,經由攝影機所擷取的即時畫面也可以應用一樣的處理方式。唯一不同的是,當程式處理速度不夠時,影片的處理時間會變長,但是攝影機的畫面就會變成跳格而錯失很多來不及處理的畫面。換句話說,如果我們希望即時處理攝影機抓取的畫面,就必須盡量提升程式的效能。對樹莓派來說,這樣的即時影像處理實在是蠻吃力的,而利用 Intel 神經運算棒 NCS/NCS2 來加速自然就是一個很合理的選擇囉。
執行範例程式
在這個 Python 範例程式中,我們僅使用到 OpenCV 這個額外的套件,而且必須是支援 OpenVINO 的特殊版本。
如果你還沒有安裝過 OpenVINO 與 OpenCV,雖然並非絕對必要,但是仍強烈建議在虛擬環境下安裝 OpenCV 與執行範例程式,而樹莓派下的安裝方法可參考之前的文章。在這裡,我沿用之前已經安裝過的樹莓派 Python 虛擬環境來執行此範例程式。不過這個程式不限於樹莓派的環境,包含 Windows 與 macOS 在內的作業系統,只要有安裝支援 OpenVINO 的 OpenCV 即可。而且程式還提供了是否使用 NCS/NCS2 的參數,因此即使手邊沒有 NCS/NCS2 也沒關係。不過如果使用樹莓派卻未搭配 NCS/NCS2,處理速度確實不是很理想,在此先提出善意的提醒。 好了,讓我們開始動手進行吧。
- 進入虛擬環境1workon openvino
- 建立所需目錄1mkdir ~/realtime_age_gender_detection; cd ~/realtime_age_gender_detection
- 啟用 OpenVINO 環境。1source /opt/intel/openvino/bin/setupvars.sh
- 下載臉孔辨識的 Caffe 模型1wget https://raw.githubusercontent.com/opencv/opencv/master/samples/dnn/face_detector/deploy.prototxt1wget https://github.com/opencv/opencv_3rdparty/raw/dnn_samples_face_detector_20170830/res10_300x300_ssd_iter_140000.caffemodel
- 下載訓練過的 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/realtime_age_gender_detection.py
- 下載包含人物的影片。1wget 'https://gcs-vimeo.akamaized.net/exp=1560369175~acl=%2A%2F1239378998.mp4%2A~hmac=2dd5f51631ecb17b571650610c53f53ec31c3d9685f55b1ac721e68fab1f971d/vimeo-prod-skyfire-std-us/01/3915/12/319576224/1239378998.mp4?download=1&filename=Pexels+Videos+1959209.mp4' -O sample.mp4
- 利用下列指令執行範例程式讀取攝影機畫面 (使用 NCS/NCS2 神經運算棒)。1python realtime_age_gender_detection.py --target vpu
或者利用下列指令執行範例程式讀取攝影機畫面 (無 NCS/NCS2 神經運算棒)。1python realtime_age_gender_detection.py
或者利用下列指令執行範例程式讀取影片 (使用 NCS/NCS2 神經運算棒)。1python realtime_age_gender_detection.py --target vpu --video sample.mp4
或者利用下列指令執行範例程式讀取影片 (無 NCS/NCS2 神經運算棒)。1python realtime_age_gender_detection.py --video sample.mp4
程式執行後我們就可以持續看到程式對影片中畫面進行臉孔偵測以及性別/年紀的辨識,如下圖所示。依據程式的判斷結果,此為二十多歲的女性臉孔:

判斷影片中人物性別與年紀
範例程式說明
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 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 | # 使用 OpenCV 內建的臉孔辨識網路抓取圖片中的臉孔,之後利用 OpenVINO IR 模型來判斷該臉孔的性別與年齡 # 載入使用到的套件 import argparse from imutils.video import VideoStream import time import cv2 # 定義程式執行時的輸入參數 ap = argparse.ArgumentParser() ap.add_argument("-v", "--video", help="path to video file. If not give, camera will be used") ap.add_argument("-t", "--target", help="which device should be used for inference") args = vars(ap.parse_args()) # 為了簡化呼叫的參數,我們直接將模型設定檔名設定為變數 # OpenVINO 年紀性別辨識網路 IR 模型檔案 openvino_age_gender_network = "age-gender-recognition-retail-0013.xml" openvino_age_gender_model = "age-gender-recognition-retail-0013.bin" # OpenCV 內建臉孔辨識網路檔案 opencv_face_model = "res10_300x300_ssd_iter_140000.caffemodel" opencv_face_network = "deploy.prototxt" # 定義模型推論結果 (機率) 所代表的性別,女生 (Female) 在前,男生 (Male) 在後。 GENDERS_FOR_OPENVINO = ['Female', 'Male'] # 分別建立臉孔與年紀/性別辨識網路 age_gender_net = cv2.dnn.readNet(openvino_age_gender_network, openvino_age_gender_model) opencv_face_net = cv2.dnn.readNetFromCaffe(opencv_face_network, opencv_face_model) # 是否使用 NCS/NCS2 進行推論 if args['target'] == 'vpu': age_gender_net.setPreferableTarget(cv2.dnn.DNN_TARGET_MYRIAD) opencv_face_net.setPreferableTarget(cv2.dnn.DNN_TARGET_MYRIAD) else: age_gender_net.setPreferableTarget(cv2.dnn.DNN_TARGET_CPU) opencv_face_net.setPreferableTarget(cv2.dnn.DNN_TARGET_CPU) # 找出圖片中的臉孔 def get_faces_from_opencv(frame): (h, w) = frame.shape[:2] blob = cv2.dnn.blobFromImage(frame, 1.0, (300, 300), (104.0, 177.0, 123.0)) opencv_face_net.setInput(blob) # 進行推論 detections = opencv_face_net.forward() faces = [] for i in range(0, detections.shape[2]): detection = detections[0, 0, i] confidence = float(detection[2]) # 當可靠度大於 0.5 (50%) 時就當作已經找到臉孔 if confidence > 0.5: xmin = max(int(detection[3] * w), 0) ymin = max(int(detection[4] * h), 0) xmax = min(int(detection[5] * w), w) ymax = min(int(detection[6] * h), h) if (xmax > xmin) and (ymax > ymin): faces.append([xmin, ymin, xmax-xmin, ymax-ymin]) # 回傳找到的臉孔 return faces # 用來判斷性別與年紀並加以標示的函式 def detect(frame, faces): # 針對圖片中的臉孔逐一處理 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) age_gender_net.setInput(blob) # 進行推論 detections = age_gender_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, (x, y-10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, color, 1) # 回傳標示過的圖片 return frame # 根據 video 參數決定開始 USB 攝影機或讀取影片檔 if not args.get("video", False): print("[INFO] starting video stream...") vs = VideoStream(src=0).start() time.sleep(2.0) else: vs = cv2.VideoCapture(args["video"]) # 開始讀取 USB 攝影機或影片畫面 while True: frame = vs.read() frame = frame[1] if args.get("video", False) else frame # 檢查是否已到影片的盡頭或因為其他原因無法讀取畫面 if frame is None: break # 取得臉孔資訊 faces = get_faces_from_opencv(frame) # 判別臉孔的年紀與性別並加以標示 frame = detect(frame, faces) # 顯示標示過後的影戲 cv2.imshow("Frame", frame) # 當使用者按下 q 鍵時跳離迴圈 key = cv2.waitKey(1) & 0xFF if key == ord('q'): break # 關閉全部視窗並釋放資源 cv2.destroyAllWindows() if not args.get("video", False): vs.stop() else: vs.release() |
- 首先我們一樣先載入程式所需的套件 (第 3-6 行),當中載入的 OpenCV (第 6 行) 套件需要支援 OpenVINO。
- 接著程式利用 argparse 套件讀入所需參數 (第 9~12 行)。我們將模型的相關檔案名稱都直接設定成變數,以減少參數的使用。
- video: 推論用的影片檔案名稱,未指定則改從攝影機抓取即時畫面。
- target: 是否使用 NCS/NCS2。當指定為 vpu 時,表示使用 NCS/NCS2 進行推論。
- 第 16~17 行定義 OpenVINO 內建的年紀與性別辨識網路的檔案名稱。
- 第 19~20 行定義 OpenCV 內建的臉孔偵測網路的檔案名稱。
- 第 22 行定義模型的性別結果輸出。這個模型的性別推論輸出為女生機率在前、男生機率在後。而年紀的推論必須乘上 100 才是網路實際推論的年紀。
- 第 26 行載入年紀與性別辨識網路,並指定為 age_gender_net。
- 第 26 行載入臉孔偵測的 Caffe 網路,並指定為 opencv_face_net。
- 第 29~34 行根據 target 參數的內容決定是否啟用 NCS/NCS2 或單純使用 CPU 進行推論。
- 第 38 行定義偵測臉孔的函式 get_faces_from_opencv,也就是此範例程式的重頭戲之一。此段程式與使用 OpenVINO 內建之臉孔偵測程式類似,主要差別在於網路的 dimension 為 (300, 300),而訓練資料平均值則為 (104.0, 177.0, 123.0)。
- 第 49 行判斷偵測到區域為臉孔的可靠度是否超過 50%,是的話才當作加入臉孔清單 (faces)。
- 第 50~53 行分別算出臉孔區域的左上 (x_min, y_min) 與右下 (x_max, y_max) 角落座標。
- 第 57 行傳回所有找到的臉孔區域。
- 第 61 行定義推論年紀與性別的函式 detect,是此範例程式的另一個重頭戲。
- 第 63 行針對所有偵測出的臉孔進行處理。
- 第 65 行使用綠色框線標示出辨識出的臉孔。
- 第 67 行則切下辨識出的臉孔。
- 因為 OpenCV 的 CNN 模組無法直接對圖片原始資訊進行處理,所以一樣透過 blobFromImage (第 69 行) 將圖片陣列轉為合適的格式,而此網路的 Spatial Dimension 則為 (62, 62)。隨後則將轉換過的資料輸入辨識網路 (第 70 行)。
- 第 72 行開始進行預測。之前我們一直使用 forward() 來取得推論結果,但是在這裡卻必須改用 forwardAndRetrieve()。原因在於性別與年紀為兩組不同的推論輸出,所以必須透過 forwardAndRetrieve 並指定輸出的名字,才能夠同時得到兩組的輸出結果。而 prob 與 age_conv3 分別為性別與年紀的輸出名稱,可以在原始文件中找到。如果我們依舊使用 forward() 來取得結果,將只會得到性別的推論。
- 第 74 行利用類似之前的方式取得代表性別的文字。
- 第 76 行計算推論的年紀。前面提到,網路輸出乘上 100 即為推測出的年紀。
- 第 80~84 行根據性別指定不同的文字顏色,並將性別與年紀標示在圖片中。
- 第 86 行回傳標示過的圖片。
- 第 90~95 行根據是否傳入 video 參數決定由 USB 攝影機或影片讀取畫面進行處理。如果你使用的是樹莓派的攝影機模組,請將 src=0 改成 usePiCamera=True。
- 第 98 行開始不斷讀取畫面。
- 影片與攝影機畫面的讀取方式有些不同,攝影機的畫面必須額外處理 (第 100 行)。
- 當影片結束或攝影機有問題而導致抓不到畫面時就中斷迴圈 (第 102~103 行)。
- 第 105 行呼叫函示找出畫面中的臉孔。
- 第 107 呼叫函示針對找到的臉孔臉孔進行年紀與性別的推論,並加以標示。
- 第 109 顯示標示後的畫面。
- 當使用者按下 q 鍵時提前中斷迴圈 (第 111~113 行)。
- 第 116~120 行在程式結束前釋放相關資源。
利用搭配 Intel 神經運算棒 NCS/NCS2 的樹莓派,我們完成了即時辨識性別與年紀的程式。快把身邊的朋友找來,看看電腦到底有多聰明,能夠正確猜到多少人的性別與年紀吧。


2 comments
Good Job! 有沒有機會跟您聯絡?
謝謝您的鼓勵。不知有何指教呢?