基於Python+Keras+OpenCV實現實時人臉活體檢測 | 文末送書

基於Python+Keras+OpenCV實現實時人臉活體檢測 | 文末送書

你在互聯網上找到的大多數人臉識別算法和研究論文都會遭受照片***。這些方法在檢測和識別來自網絡攝像頭的圖像、視頻和視頻流中的人臉方面是很是有效,可是他們沒法區分現實生活中的面孔和照片上的面孔。這種沒法區別現實人臉的現象是因爲這些算法是在二維幀上工做的。
如今讓咱們去試想一下,咱們實現一我的臉識別系統,該系統能夠很好地區分已知面孔和未知面孔,以便只有受權人員才能訪問,儘管如此,一個心懷不軌的人只要出示受權人的照片也能訪問。至此一個3D人臉的識別系統,相似於蘋果的FaceID,應運而生了,但若是咱們沒有3D探測器該怎麼辦呢?
基於Python+Keras+OpenCV實現實時人臉活體檢測 | 文末送書
本文的目標是實現一種基於眨眼檢測的人臉活體檢測算法,以抵抗照片***。該算法經過網絡攝像頭實時工做,經過檢測眨眼來區分現實生活中的面孔和照片上的面孔。通俗地說,程序運行以下:html

  • 在網絡攝像頭生成的每一個幀中檢測人臉。
  • 對於每一個檢測到的臉,檢測眼睛。
  • 對於每一個檢測到的眼睛,檢測眼睛是否睜開或關閉。
  • 若是在某個時候檢測到眼睛是睜開的,而後是閉着的,而後是睜開的,咱們就判定此人已經眨了眼睛,而且程序顯示他的名字(若是是人臉識別開門器,咱們將受權此人進入)。
    對於人臉的檢測和識別,你須要安裝face_recognition庫,它提供了很是有用的深度學習方法來查找和識別圖像中的人臉,特別是,face_locations、face_encodings和compare_faces函數是最有用的3個函數。人臉定位方法能夠用兩種方法來檢測人臉:方向梯度直方圖(HoG)和卷積神經網絡(CNN),因爲時間限制,選擇了HoG方法。
    face_encodings函數是一個預先訓練的卷積神經網絡,可以將圖像編碼成128哥元素的一維特徵向量,這個嵌入向量包含足夠的特徵信息來區分兩個不一樣的人,最後,compare_faces計算兩個嵌入向量之間的距離。它將容許算法識別從攝像頭幀中提取的人臉,並將其嵌入向量與咱們數據集中全部編碼的人臉進行比較,距離最近的向量對應於同一我的。
    1. 已知人臉數據集編碼
    在個人例子中,算法可以識別我和奧巴馬,我爲每一個人挑選了大約10張照片。下面是處理和編碼已知人臉數據庫的代碼。

def process_and_encode(images):
known_encodings = []
known_names = []
print("[LOG] Encoding dataset ...")python

for image_path in tqdm(images):
    # 加載圖片
    image = cv2.imread(image_path)
    # 將其從BGR轉換爲RGB
    image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)

    # 檢測圖像中的臉並獲取其位置(方框座標)
    boxes = face_recognition.face_locations(image, model='hog')

    # 將人臉編碼爲128維嵌入向量
    encoding = face_recognition.face_encodings(image, boxes)

    # 人物名稱是圖像來源文件夾的名稱
    name = image_path.split(os.path.sep)[-2]

    if len(encoding) > 0 : 
        known_encodings.append(encoding[0])
        known_names.append(name)

return {"encodings": known_encodings, "names": known_names}
如今咱們知道了每一個想識別的人的編碼,咱們能夠嘗試經過網絡攝像頭識別人臉,然而,在轉到這一部分以前,咱們須要區分一張人臉照片和一張活人的臉。
**2.人臉活體檢測**
咱們的目標是在某個點上檢測出一個睜閉的睜眼模式。我訓練了一個卷積神經網絡來分類眼睛是閉着的仍是睜着的,所選擇的模型是LeNet-5,它已經在Closed Eyes In The Wild (CEW)數據集上進行了訓練,它由大約4800張24x24大小的眼睛圖像組成。
Closed Eyes In The Wild (CEW)數據集地址:
* http://parnec.nuaa.edu.cn/xtan/data/ClosedEyeDatabases.html

from keras.models import Sequential
from keras.layers import Conv2D
from keras.layers import AveragePooling2D
from keras.layers import Flatten
from keras.layers import Dense
from keras.preprocessing.image import ImageDataGeneratorgit

IMG_SIZE = 24
def train(train_generator, val_generator):
STEP_SIZE_TRAIN=train_generator.n//train_generator.batch_size
STEP_SIZE_VALID=val_generator.n//val_generator.batch_sizegithub

model = Sequential()

model.add(Conv2D(filters=6, kernel_size=(3, 3), activation='relu', input_shape=(IMG_SIZE,IMG_SIZE,1)))
model.add(AveragePooling2D())

model.add(Conv2D(filters=16, kernel_size=(3, 3), activation='relu'))
model.add(AveragePooling2D())

model.add(Flatten())

model.add(Dense(units=120, activation='relu'))

model.add(Dense(units=84, activation='relu'))

model.add(Dense(units=1, activation = 'sigmoid'))

model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])

print('[LOG] Training CNN')

model.fit_generator(generator=train_generator,
                    steps_per_epoch=STEP_SIZE_TRAIN,
                    validation_data=val_generator,
                    validation_steps=STEP_SIZE_VALID,
                    epochs=20
)

return model算法

在評估模型時,我達到了94%的準確率。
每次咱們檢測到一隻眼睛,咱們就用咱們的模型來預測它的狀態,並跟蹤每一個人的眼睛狀態,所以,檢測眨眼變得很是容易,它試圖在眼睛狀態歷史中找到一個閉眼-睜眼-閉眼的過程。

def isBlinking(history, maxFrames):
""" @history: A string containing the history of eyes status
where a '1' means that the eyes were closed and '0' open.
@maxFrames: The maximal number of successive frames where an eye is closed """
for i in range(maxFrames):
pattern = '1' + '0'*(i+1) + '1'
if pattern in history:
return True
return False數據庫

**3.活體的人臉識別**
咱們幾乎擁有了創建「真實」人臉識別算法的全部要素,咱們只須要一種實時檢測人臉和眼睛的方法。我使用openCV預先訓練的Haar級聯分類器來完成這些任務。有關Haar cascade人臉和眼睛檢測的更多信息,我強烈建議你閱讀openCV的這篇強大的文章。
* https://docs.opencv.org/3.4.3/d7/d8b/tutorial_py_face_detection.html
*

def detect_and_display(model, video_capture, face_detector, open_eyes_detector, left_eye_detector, right_eye_detector, data, eyes_detected):
frame = video_capture.read()網絡

調整框架大小

frame = cv2.resize(frame, (0, 0), fx=0.6, fy=0.6)

    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)

    # 檢測人臉
    faces = face_detector.detectMultiScale(
        gray,
        scaleFactor=1.2,
        minNeighbors=5,
        minSize=(50, 50),
        flags=cv2.CASCADE_SCALE_IMAGE
    )

    # 對於每一個檢測到的臉
    for (x,y,w,h) in faces:
        # 將人臉編碼爲128維嵌入向量
        encoding = face_recognition.face_encodings(rgb, [(y, x+w, y+h, x)])[0]

        # 將向量與全部已知的人臉編碼進行比較
        matches = face_recognition.compare_faces(data["encodings"], encoding)

        # 目前咱們不知道該人的名字
        name = "Unknown"

        # 若是至少有一次匹配:
        if True in matches:
            matchedIdxs = [i for (i, b) in enumerate(matches) if b]
            counts = {}
            for i in matchedIdxs:
                name = data["names"][i]
                counts[name] = counts.get(name, 0) + 1

            # 匹配次數最多的已知編碼對應於檢測到的人臉名稱
            name = max(counts, key=counts.get)

        face = frame[y:y+h,x:x+w]
        gray_face = gray[y:y+h,x:x+w]

        eyes = []

        # 眼睛檢測
        # 首先檢查眼睛是否睜開(考慮到眼鏡)
        open_eyes_glasses = open_eyes_detector.detectMultiScale(
            gray_face,
            scaleFactor=1.1,
            minNeighbors=5,
            minSize=(30, 30),
            flags = cv2.CASCADE_SCALE_IMAGE
        )
        # 若是open_eyes_glasses檢測到眼睛,則眼睛睜開 
        if len(open_eyes_glasses) == 2:
            eyes_detected[name]+='1'
            for (ex,ey,ew,eh) in open_eyes_glasses:
                cv2.rectangle(face,(ex,ey),(ex+ew,ey+eh),(0,255,0),2)

        # 不然嘗試使用left和right_eye_detector檢測眼睛
        # 以檢測到睜開和閉合的眼睛                
        else:
            # 將臉分紅左右兩邊
            left_face = frame[y:y+h, x+int(w/2):x+w]
            left_face_gray = gray[y:y+h, x+int(w/2):x+w]

            right_face = frame[y:y+h, x:x+int(w/2)]
            right_face_gray = gray[y:y+h, x:x+int(w/2)]

            # 檢測左眼
            left_eye = left_eye_detector.detectMultiScale(
                left_face_gray,
                scaleFactor=1.1,
                minNeighbors=5,
                minSize=(30, 30),
                flags = cv2.CASCADE_SCALE_IMAGE
            )

            # 檢測右眼
            right_eye = right_eye_detector.detectMultiScale(
                right_face_gray,
                scaleFactor=1.1,
                minNeighbors=5,
                minSize=(30, 30),
                flags = cv2.CASCADE_SCALE_IMAGE
            )

            eye_status = '1' # we suppose the eyes are open

            # 檢查每隻眼睛是否閉合。
            # 若是有人閉着眼睛,咱們得出結論是閉着眼睛
            for (ex,ey,ew,eh) in right_eye:
                color = (0,255,0)
                pred = predict(right_face[ey:ey+eh,ex:ex+ew],model)
                if pred == 'closed':
                    eye_status='0'
                    color = (0,0,255)
                cv2.rectangle(right_face,(ex,ey),(ex+ew,ey+eh),color,2)
            for (ex,ey,ew,eh) in left_eye:
                color = (0,255,0)
                pred = predict(left_face[ey:ey+eh,ex:ex+ew],model)
                if pred == 'closed':
                    eye_status='0'
                    color = (0,0,255)
                cv2.rectangle(left_face,(ex,ey),(ex+ew,ey+eh),color,2)
            eyes_detected[name] += eye_status

        # 每次,咱們都會檢查該人是否眨眼
        # 若是是,咱們顯示其名字
        if isBlinking(eyes_detected[name],3):
            cv2.rectangle(frame, (x, y), (x+w, y+h), (0, 255, 0), 2)
            # 顯示名字
            y = y - 15 if y - 15 > 15 else y + 15
            cv2.putText(frame, name, (x, y), cv2.FONT_HERSHEY_SIMPLEX,0.75, (0, 255, 0), 2)

    return frame
上面的功能是用於檢測和識別真實人臉的代碼,它接受如下參數:
* model:咱們的睜眼/閉眼分類器
* video_capture:流視頻
* face_detector:Haar級聯的人臉分類器。我使用了haarcascade_frontalface_alt.xml
* open_eyes_detector:Haar級聯睜眼分類器。我使用了haarcascade_eye_tree_eyeglasses.xml
* left_eye_detector:Haar級聯的左眼分類器。我使用了haarcascade_lefteye_2splits.xml,它能夠檢測睜眼或閉眼。
* right_eye_detector:Haar級聯的右眼分類器。我使用了haarcascade_righteye_2splits.xml,它能夠檢測睜眼或閉眼。
* data:已知編碼和已知名稱的字典
* eyes_detected:包含每一個名稱的眼睛狀態歷史記錄的字典。
在第2-4行,咱們從網絡攝像頭流中獲取一個幀,而後調整其大小以加快計算速度。
在第10行,咱們從幀中檢測人臉,而後在第21行,咱們將其編碼爲128-d矢量。
在第23-38行,咱們將這個向量與已知的人臉編碼進行比較,並經過計算匹配的次數來肯定此人的姓名,選擇匹配次數最多的一個。
從第45行開始,咱們試着探測眼睛進入人臉框。
首先,咱們嘗試用睜眼檢測器來檢測睜眼,若是探測器探測成功,則在第54行,將「1」添加到眼睛狀態歷史記錄中,這意味着眼睛是睜開的,由於睜開的眼睛探測器沒法檢測到閉着的眼睛;不然,若是第一個分類器失敗(多是由於眼睛是閉着的,或者僅僅是由於它不能識別眼睛),則使用左眼和右眼檢測器,人臉被分爲左右兩側,以便對各個探測器進行分類。
從第92行開始,提取眼睛部分,訓練後的模型預測眼睛是否閉合,若是檢測到一隻眼睛閉着,則兩眼都將被預測爲閉着,並將「0」添加到眼睛狀態歷史記錄中;不然就能夠判定眼睛是睜開的。
最後,在第110行,is blinking()函數用於檢測眨眼,若是該人眨眼,則顯示姓名。整個代碼均可以在個人github賬戶上找到。
* 個人github賬戶
* https://github.com/Guarouba/face_rec
使用眨眼檢測功能阻止照片***的演示視頻:https://youtu.be/arQN6w0fZw8
**參考文獻**
* https://docs.opencv.org/3.4.3/d7/d8b/tutorial_py_face_detection.html
* https://www.pyimagesearch.com/2018/06/18/face-recognition-with-opencv-python-and-deep-learning/
原文連接:https://towardsdatascience.com/real-time-face-liveness-detection-with-python-keras-and-opencv-c35dc70dafd3
相關文章
相關標籤/搜索