這裡示範如何在 Python 中使用 OpenCV 與 Dlib 開發人臉偵測程式,即時擷取網路攝影機串流影像,輸出人臉偵測結果。
人臉偵測是一項相當成熟的技術,不管是數位相機或是手機在拍照時,都可以自動偵測人臉並對焦,而在自行開發的程式當中,若要加入人臉偵測的功能也非常容易,只要串接相關的模組即可實作出相當專業的程式。
人臉偵測的實作方式有很多種,這裡我們選擇使用 Dlib 這套機器學習函式庫所提供的人臉偵測,以 Python 來撰寫一個可以自動偵測照片或影片中所有人臉位置的程式。以下是一個最簡單的人臉偵測程式碼:
import dlib import cv2 import imutils # 讀取照片圖檔 img = cv2.imread('image.jpg') # 縮小圖片 img = imutils.resize(img, width=1280) # Dlib 的人臉偵測器 detector = dlib.get_frontal_face_detector() # 偵測人臉 face_rects = detector(img, 0) # 取出所有偵測的結果 for i, d in enumerate(face_rects): x1 = d.left() y1 = d.top() x2 = d.right() y2 = d.bottom() # 以方框標示偵測的人臉 cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 4, cv2.LINE_AA) # 顯示結果 cv2.imshow("Face Detection", img) cv2.waitKey(0) cv2.destroyAllWindows()
這裡我們使用 OpenCV 讀取一張照片(請參考 OpenCV 的圖檔存取教學),將圖片縮小之後(亦可直接用原圖),再交給 Dlib 的人臉偵測器來偵測圖片中所有人臉的位置,最後再將所有偵測出的人臉位置用綠色方框標示出來。
Dlib 使用的人臉偵測演算法是以方向梯度直方圖(HOG)的特徵加上線性分類器(linear classifier)、影像金字塔(image pyramid)與滑動窗格(sliding window)來實作的。
detector
函數的第二個參數是指定反取樣(unsample)的次數,如果圖片太小的時候,將其設為 1
可讓程式偵較容易測出更多的人臉。
我們以下面這張照片作為範例,嘗試以上面的 Python 程式來偵測其中的人臉位置。
程式執行之後,就會開出一個 OpenCV 視窗,顯示人臉偵測的結果。
事實上每一個人臉偵測的結果都會對應到一個分數,分數越高代表該偵測結果越可能是真的人臉,反之若分數很低的話,就比較有可能是誤判的結果。
在程式開發階段,我們可以將所有的偵測結果與分數都顯示出來,這樣在除錯上也會比較方便,以下是一個簡單的範例:
import dlib import cv2 import imutils img = cv2.imread('human-20180303-01.jpg') img = imutils.resize(img, width=1280) detector = dlib.get_frontal_face_detector() # 偵測人臉,輸出分數 face_rects, scores, idx = detector.run(img, 0, -1) for i, d in enumerate(face_rects): x1 = d.left() y1 = d.top() x2 = d.right() y2 = d.bottom() text = "%2.2f(%d)" % (scores[i], idx[i]) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 4, cv2.LINE_AA) # 標示分數 cv2.putText(img, text, (x1, y1), cv2.FONT_HERSHEY_DUPLEX, 0.7, (255, 255, 255), 1, cv2.LINE_AA) cv2.imshow("Face Detection", img) cv2.waitKey(0) cv2.destroyAllWindows()
這裡我們改用 detector.run
來偵測人臉,它的第三個參數是指定分數的門檻值,所有分數超過這個門檻值的偵測結果都會被輸出,而傳回的結果除了人臉的位置之外,還有分數(scores
)與子偵測器的編號(idx
),子偵測器的編號可以用來判斷人臉的方向,請參考 Dlib 的說明。
以下是程式的執行結果(點擊圖片查看原圖會看得比較清楚):
若要偵測影片中的人臉,可以使用 OpenCV 取出影片中的每張影格(請參考 OpenCV 處理影片檔案的教學),再交給 Dlib 偵測人臉,以下是簡單的範例:
import dlib import cv2 import imutils # 開啟影片檔案 cap = cv2.VideoCapture('video.mp4') # 取得畫面尺寸 width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)) height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) # 使用 XVID 編碼 fourcc = cv2.VideoWriter_fourcc(*'XVID') # 建立 VideoWriter 物件,輸出影片至 output.avi,FPS 值為 20.0 out = cv2.VideoWriter('output.avi', fourcc, 20.0, (width, height)) # Dlib 的人臉偵測器 detector = dlib.get_frontal_face_detector() # 以迴圈從影片檔案讀取影格,並顯示出來 while(cap.isOpened()): ret, frame = cap.read() # 偵測人臉 face_rects, scores, idx = detector.run(frame, 0) # 取出所有偵測的結果 for i, d in enumerate(face_rects): x1 = d.left() y1 = d.top() x2 = d.right() y2 = d.bottom() text = "%2.2f(%d)" % (scores[i], idx[i]) # 以方框標示偵測的人臉 cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 255, 0), 4, cv2.LINE_AA) # 標示分數 cv2.putText(frame, text, (x1, y1), cv2.FONT_HERSHEY_DUPLEX, 0.7, (255, 255, 255), 1, cv2.LINE_AA) # 寫入影格 out.write(frame) # 顯示結果 cv2.imshow("Face Detection", frame) if cv2.waitKey(1) & 0xFF == ord('q'): break cap.release() out.release() cv2.destroyAllWindows()
這裡我將即時的處理畫面顯示在視窗中,並且同時寫入 output.avi
這個影片檔,而寫入時的 FPS 降為 20.0
,這樣比較好觀察結果。
若要偵測串流影像中的人臉,也是一樣將影格取出,交給 Dlib 處理即可,以下是從網路攝影機擷取影像並偵測人臉的範例:
import dlib import cv2 import imutils # 開啟影片檔案 cap = cv2.VideoCapture(0) # Dlib 的人臉偵測器 detector = dlib.get_frontal_face_detector() # 以迴圈從影片檔案讀取影格,並顯示出來 while(cap.isOpened()): ret, frame = cap.read() # 偵測人臉 face_rects, scores, idx = detector.run(frame, 0) # 取出所有偵測的結果 for i, d in enumerate(face_rects): x1 = d.left() y1 = d.top() x2 = d.right() y2 = d.bottom() text = "%2.2f(%d)" % (scores[i], idx[i]) # 以方框標示偵測的人臉 cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 255, 0), 4, cv2.LINE_AA) # 標示分數 cv2.putText(frame, text, (x1, y1), cv2.FONT_HERSHEY_DUPLEX, 0.7, (255, 255, 255), 1, cv2.LINE_AA) # 顯示結果 cv2.imshow("Face Detection", frame) if cv2.waitKey(1) & 0xFF == ord('q'): break cap.release() cv2.destroyAllWindows()
執行後就會顯示即時串流影像的人臉偵測結果。
參考資料:Codemade.io、OpenCV、Dlib