Обнаружение живости лица в реальном времени на основе Python, Keras и OpenCV

машинное обучение глубокое обучение компьютерное зрение NLP

Автор | Джордан Ван Этвельдт Компиляция|Флин Источник|наука о данных

Большинство алгоритмов распознавания лиц и исследовательских работ, которые вы найдете в Интернете, страдают от фотоатак. Эти методы очень эффективны при обнаружении и распознавании лиц на изображениях, видео и видеопотоках с веб-камер. Однако они не могли отличить лица в реальной жизни от лиц на фотографиях. Эта неспособность распознавать лица связана с тем, что эти алгоритмы работают с 2D-кадрами.

Теперь давайте представим, что мы хотим реализовать функцию открывания дверей с распознаванием лиц. Система хорошо различает известные и неизвестные лица, поэтому доступ к ним может получить только авторизованный персонал. Тем не менее, человеку с плохими намерениями достаточно показать фотографию авторизатора. Появился этот 3D-детектор, похожий на Apple FaceID. Но что, если у нас нет 3D-детектора?

Целью этой статьи является реализация алгоритма определения живости лица на основе обнаружения моргания глаз для защиты от фотоатак. Алгоритм работает в режиме реального времени через веб-камеру и появляется только тогда, когда мигает имя человека. С точки зрения непрофессионала, программа работает следующим образом:

  1. Обнаружение лиц в каждом кадре, созданном веб-камерой.
  2. Для каждого обнаруженного лица определите глаза.
  3. Для каждого обнаруженного глаза определите, открыт он или закрыт.
  4. Если в какой-то момент обнаруживается, что глаза то открыты, то закрыты, то открыты, делаем вывод, что человек моргнул и программа выводит его имя (если это открыватель дверей с распознаванием лиц, то авторизуем этого человека на вход) .

Для обнаружения и распознавания лиц вам необходимо установить библиотеку face_recognition, которая предоставляет очень полезные методы глубокого обучения для поиска и распознавания лиц на изображениях. В частности, функции face_locations, face_encodings и compare_faces являются тремя наиболее полезными функциями. Методы локализации лиц могут обнаруживать лица двумя способами: гистограмма ориентированных градиентов (HoG) и сверточные нейронные сети (CNN). Из-за нехватки времени был выбран метод HoG.

Функция face_encodings — это предварительно обученная сверточная нейронная сеть, способная кодировать изображения в 128 векторов признаков. Этот вектор встраивания должен представлять достаточно информации, чтобы различать двух разных людей. Наконец, compare_faces вычисляет расстояние между двумя векторами встраивания. Это позволит алгоритму идентифицировать лица, извлеченные из кадров камеры, и сравнить их векторы встраивания со всеми закодированными лицами в нашем наборе данных. Ближайший вектор должен соответствовать одному и тому же человеку.

1. Известная кодировка набора данных о лицах

В моем случае алгоритм смог идентифицировать меня и Обаму. Я выбрал около 10 фотографий для каждого. Ниже приведен код для обработки и кодирования базы данных известных лиц.

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

    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}

Теперь, когда мы знаем код каждого человека, которого хотим идентифицировать, мы можем попытаться идентифицировать лица с веб-камеры. Однако, прежде чем перейти к этой части, нам необходимо различать фотографию человеческого лица и лицо живого человека.

  1. Обнаружение живости лица

Напоминаем, что наша цель — в какой-то момент обнаружить паттерн открытых и закрытых глаз. Я обучил сверточную нейронную сеть классифицировать, закрыты глаза или открыты. Выбранная модель — LeNet-5, обученная на наборе данных Closed Eyes In The Wild (CEW). Он состоит из около 4800 изображений глаз размером 24x24.

Адрес набора данных Closed Eyes In The Wild (CEW):

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 ImageDataGenerator

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_size
	
	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
  1. Распознавание живых лиц

У нас есть почти все ингредиенты для создания «настоящего» алгоритма распознавания лиц. Нам просто нужен способ обнаружения лиц и глаз в режиме реального времени. Для этих задач я использую предварительно обученный каскадный классификатор Хаара openCV. Для получения дополнительной информации о каскадном обнаружении лица и глаз Хаара я настоятельно рекомендую вам прочитать эту мощную статью на openCV.

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

Функция выше — это код для обнаружения и распознавания реальных лиц. Он принимает следующие параметры:

  • модель: наш классификатор открытых/закрытых глаз

  • video_capture: потоковое видео

  • face_detector: каскадный классификатор лиц Хаара. Я использовал haarcascade_frontalface_alt.xml

  • open_eyes_detector: каскадный классификатор открытых глаз Хаара. Я использовал файл haarcascade_eye_tree_eyeglasses.xml.

  • left_eye_detector: каскадный классификатор левого глаза Хаара. Я использовал haarcascade_lefteye_2splits.xml, и он обнаруживает открытые или закрытые глаза.

  • right_eye_detector: каскадный классификатор правого глаза Хаара. Я использовал haarcascade_righteye_2splits.xml, и он обнаруживает открытые или закрытые глаза.

  • data: словарь известных кодировок и известных имён

  • eyes_detected: словарь, содержащий историю состояния глаз для каждого имени.

существуетСтроки 2-4, мы берем кадр из потока с веб-камеры и масштабируем его, чтобы ускорить вычисления.

существуетстрока 10, мы обнаруживаем лицо из кадра, затем в строке 21 мы кодируем его как 128-мерный вектор.

существуетСтроки 23-38, мы сравниваем этот вектор с известными кодировками лица и определяем имя человека, подсчитывая количество совпадений. Выберите тот, у которого больше всего совпадений.

отстрока 45Для начала мы пытаемся обнаружить глаза, попадающие в рамку лица.

Сначала мы пытаемся обнаружить открытые глаза с помощью детектора открытых глаз. Если детектор прошел успешно, в строке 54 добавьте «1» к истории состояния глаза, что означает, что глаз открыт, потому что детектор открытого глаза не может обнаружить закрытый глаз. В противном случае, если первый классификатор дает сбой (возможно, из-за того, что глаза закрыты или просто потому, что он не может идентифицировать глаза), используются детекторы левого и правого глаза. Лицо разделено на левую и правую стороны для классификации отдельных детекторов.

отстрока 92Для начала извлекается часть глаза, и обученная модель предсказывает, закрыт глаз или нет. Если обнаружено, что один глаз закрыт, прогнозируется, что оба глаза будут закрыты, и в историю состояния глаза будет добавлен «0». В противном случае можно сделать вывод, что глаза открыты.

Наконец, встрока 110, функция is blinking() используется для обнаружения моргания, и если человек моргает, отображается имя. Весь код доступен в моей учетной записи github.

Демонстрационное видео блокировки фотоатак с обнаружением моргания:youtu.be/arQN6w0fZw8

использованная литература

Оригинальная ссылка:к data science.com/real-time-post…

Добро пожаловать на сайт блога Panchuang AI:panchuang.net/

sklearn машинное обучение китайские официальные документы:sklearn123.com/

Добро пожаловать на станцию ​​сводки ресурсов блога Panchuang:docs.panchuang.net/