這裡示範如何在 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.ioOpenCVDlib